题解 P5207 【[WC2019] 远古计算机】

· · 题解

一道轻松愉快的小水题,只要会最短路就能做

别看题面上说了那么多指令,有用的其实就4个:read,write,add,jmp

接下来就分别说一下每个子任务的做法

任务一

评分参数:200,300,10000

注意到执行完最后一句后会回到第一句执行,因此直接read/write即可

答案如下:

node 1
read 0 a
write a 0

任务二

评分参数:4,5,50

显然需要一个O(1)的算法才能通过

直接打表,将第0到44个fib数打出来,然后读入k后直接jump到对应的行输出即可

以下是答案的生成代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
    freopen("oldcomputer2.out","w",stdout);
    printf("node 1\n");
    printf("read 0 a\n");
    printf("add a 4\n");
    printf("jmp a\n");
    static int f[45];
    f[0]=0;f[1]=1;
    for(int i=2;i<=45;i++)
        f[i]=f[i-1]+f[i-2];
    for(int i=0;i<=45;i++)
        printf("write %d 0\n",f[i]);
}

任务三

评分参数:211,422,5000

实际上是要将一些信息从1传到n。注意到多路传输并不能减少轮数,因为最后肯定会卡在某个点,因此我们选择单路传输

单路传输的最快方案就是找一条1到n的最短路,因此直接跑单源最短路即可

以下是答案的生成代码:

#include<bits/stdc++.h>
using namespace std;

const int N=110;
vector<int> g[N];
bool inq[N];
int dis[N],pre[N],n,m;

void spfa()
{
    memset(dis,0x3f,sizeof(dis));
    queue<int> q;
    q.push(1);dis[1]=0;
    while(!q.empty())
    {
        int u=q.front();
        for(int v : g[u])
            if(dis[u]+1<dis[v])
            {
                pre[v]=u;
                dis[v]=dis[u]+1;
                if(inq[v]) continue;
                inq[v]=1;q.push(v);
            }
        q.pop();inq[u]=0;
    }
    for(int i=n,nxt=0;i;i=pre[i])
    {
        printf("node %d\n",i);
        printf("read %d a\n",pre[i]);
        printf("write a %d\n",nxt);
        nxt=i;
    }
}

int main()
{
    freopen("oldcomputer3.in","r",stdin);
    freopen("oldcomputer3.out","w",stdout);
    int qwq;
    scanf("%d%d%d",&qwq,&n,&m);
    for(int i=1,u,v;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    spfa();
}

任务四

评分参数:7,12,200

这个任务要求我们进行多路传输

注意到有1000个点,因此我们很容易能找到非常短的传输路径。我们可以跑一遍多源最短路(51到100号节点为源),然后将1到50号节点入队进行BFS。对于一个节点u,若存在一个节点v满足dis[u]=dis[v]+1,则将u的全部信息传递给v

因为一个节点可能需要处理多路信息,因此我们在BFS时对传递的信息加一个时间戳,一个点有多路信息时,按时间顺序处理这些信息

以下是答案生成代码:

#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> pii;
const int N=1010;
vector<int> g[N];
vector<pii> msg[N];
int dis[N],n,m;
bool inq[N];

void spfa()
{
    queue<int> q;
    memset(dis,0x3f,sizeof(dis));
    for(int i=51;i<=100;i++)
    {
        dis[i]=0;
        inq[i]=1;
        q.push(i);
    }
    while(!q.empty())
    {
        int u=q.front();
        for(int v : g[u])
            if(dis[u]+1<dis[v])
            {
                dis[v]=dis[u]+1;
                if(inq[v]) continue;
                inq[v]=1;q.push(v);
            }
        q.pop();inq[u]=0;
    }
}

int main()
{
    freopen("oldcomputer4.in","r",stdin);
    freopen("oldcomputer4.out","w",stdout);
    int qwq;
    scanf("%d%d%d",&qwq,&n,&m);
    for(int i=1,u,v;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    spfa();
    queue<int> q;
    for(int i=1;i<=50;i++)
    {
        q.push(i);
        msg[i].push_back(pii(0,0));
    }
    while(!q.empty())
    {
        int u=q.front(),v=0;q.pop();
        for(int vv : g[u]) if(dis[u]==dis[vv]+1) v=vv;
        printf("node %d\n",u);
        sort(msg[u].begin(),msg[u].end());
        for(pii pr : msg[u])
        {
            printf("read %d a\n",pr.second);
            printf("write a %d\n",v);
            msg[v].push_back(pii(pr.first+1,u));
        }
        if(v) q.push(v);
    }
    return 0;
}

任务五

评分参数:21,22,31

还是多路信息传递,不过这次我们有了明确的信息传递目标

我们可以对每个节点,记录一下占用情况。具体地,当bad[u][t]=1时,说明节点u在时间点t时被占用

这样,在我们跑最短路时,若当前节点是u,需要用他的距离去更新v。w可以用来更新dis[v],当且仅当w和w+1时刻,v节点均为空闲状态。因此我们可以设w初值为dis[u]+1,然后不断自增直到满足上述条件,再来更新dis[v]

跑完一遍最短路后,要记得更新s到t最短路径上的所有点的占用状态

按上述方法跑10遍单源最短路即可

#include<bits/stdc++.h>
using namespace std;

const int N=210;
struct MSG{int from,to,time;};
vector<int> g[N];
vector<MSG> msg[N];
bool bad[N][N],inq[N];
int dis[N],pre[N],n,m,ans=0;

bool operator < (const MSG &a,const MSG &b){return a.time<b.time;}

void spfa(int s,int t)
{
    memset(pre,0,sizeof(pre));
    memset(dis,0x3f,sizeof(dis));
    queue<int> q;q.push(s);
    dis[s]=0;inq[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        for(int v : g[u])
        {
            int w=dis[u]+1;
            while(bad[v][w]||bad[v][w+1]) w++;
            if(w<dis[v])
            {
                dis[v]=w;pre[v]=u;
                if(inq[v]) continue;
                inq[v]=1;q.push(v);
            }
        }
        q.pop();inq[u]=0;
    }
    for(int i=t,nxt=0;i;i=pre[i])
    {
        bad[i][dis[i]]=bad[i][dis[i]+1]=1;
        msg[i].push_back((MSG){pre[i],nxt,dis[i]});
        nxt=i;
    }
    ans=max(ans,dis[t]);
}

int main()
{
    freopen("oldcomputer5.in","r",stdin);
    freopen("oldcomputer5.out","w",stdout);
    int qwq;
    scanf("%d%d%d",&qwq,&n,&m);
    for(int i=1,u,v;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    for(int i=1;i<=10;i++) bad[i][0]=bad[i][1]=1;
    for(int i=1;i<=10;i++) spfa(i,101-i);
    for(int i=1;i<=n;i++)
    {
        if(msg[i].empty()) continue;
        sort(msg[i].begin(),msg[i].end());
        printf("node %d\n",i);
        for(MSG pp : msg[i])
        {
            printf("read %d a\n",pp.from);
            printf("write a %d\n",pp.to);
        }
    }
    cerr<<ans+2<<endl;
    return 0;
}