继上篇博客,有个拆点的写法。
众所周知:dp是有向图上最短路。
拆点怎么拆?
把一个点拆成k + 1个点表示之前已经走了x步的最短距离。
然后像dp的那样建图就好了,两者实质上是一样的。拆点只不过是把每个状态都表示成了一个点,然后bfs 跑最短路。
记得特判自己没有出边的情况。
#include<stdio.h>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define mkp make_pair
#define st first
#define sd second
const int maxn = 6e6 + 13;
const int inf = 0x3f3f3f3f;
const int M = 50+5;
struct Edge
{
int id,nex;
}edge[maxn * 3];
int head[maxn];
int dis[maxn];
int cnt;
void add(int x,int y)
{
edge[cnt].id = y;
edge[cnt].nex = head[x];
head[x] = cnt ++ ;
}
int vis[maxn];
queue<int> qq;
int du[maxn];
int main()
{
int T;
scanf("%d",&T);
int cas = 1;
while(T -- )
{
cnt = 0;
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i= 0; i <= n * (k + 1); i ++ )
{
head[i] = - 1;
vis[i] = 0;
dis[i] = inf;
du[i] = 0;
}
for(int i= 1; i<= m; i ++ )
{
int x,y;
scanf("%d%d",&x,&y);
for(int j = 1; j <= k; j ++ )
{
add(x + j * n,y + (j - 1) * n);
add(y + j * n,x + (j - 1) * n);
}
for(int j = 0; j <= k; j ++ )
{
add(y,x + j * n);
du[x + j * n] ++ ;
// 因为要跑距离最远的所以记一下度数,最后一次到达这个点就是最远的。
}
}
for (int i= 1; i<= n; i ++ )
{
if(du[i] == 0)
{
for(int j = 0; j <= k; j ++ )
{
add(i,i + j * n);
du[i + j * n] ++ ;// 这里也要记,忘了就wa
}
}
}
for(int j = 0; j <= k; j ++ )
{
qq.push(n + j * n);
dis[n + j * n] = 0;
vis[n + j * n]= 1;
}
while(!qq.empty())
{
int x = qq.front();
qq.pop();
// printf("%d\n",x);
if((x - 1) / n == 0)
{
for(int i = head[x]; ~i; i = edge[i].nex)
{
int v= edge[i].id;
du[v] -- ;
if(du[v] == 0)
{
if(vis[v] == 0)
{
dis[v] = dis[x] + 1;
vis[v] = 1;
qq.push(v);
}
}
}
continue;
}
for (int i = head[x]; ~i; i = edge[i].nex)
{
int v = edge[i].id;
if(vis[v] == 0)
{
vis[v] = 1;
dis[v] = dis[x] + 1;
qq.push(v);
}
}
}
printf("Case #%d:\n",cas ++ );
for (int i= 1; i <= n; i ++ )
{
int ans = inf;
for(int j = 0; j <= k; j ++ )
{
ans = min(ans,dis[i + j * n]);
}
if(ans == inf)
printf("-1\n");
else
printf("%d\n",ans);
}
}
}
顺便骂一句 cao 老子的外卡