CCPC 绵阳 7-5 Escape from the Island 拆点解法

继上篇博客,有个拆点的写法。

众所周知: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 老子的外卡

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值