E. Egor in the Republic of Dagestan(DAG最短路+dp)详解

https://codeforces.com/contest/1407/problem/E


题意:给一个有向图,每条边有一个类型(0或者1),什么类型的城市就只能走什么类型的边,你需要给每个城市指定类型(0/1),使得从1到n 的最短路最长,甚至长到1无法到达n

思路:开始想的时候感觉是个dp,而且要反向建边。这道题补了挺久的。

首先要明确这是个dp,那么dp的话正常开始是正向入手,但是正向很难处理,为什么呢?

比如 1---(0,1)---->2----(1,0)---->3从1转移到2节点的时候,2节点取0/1取决后面的2后面的边权,没法很好确定2这个点最后该取哪色

                             |------(1,0)------->4

但是反向建边就可以保证用边权构造点的颜色。比如 2<----(0)-----4,那么可以确定2是0。或者2<------(1,0)----4,可以保证2可以取1/0;

而且这个题你会发现,只有当一个点同时被1和0都更新过了,才能保证这个图必然是能最终连通的。

比如我2这个节点反向边上只有0,那么我正图中操作的时候令其为1,那么最后就能让答案是-1。

那么考虑dp。

b[x]:x点为黑色,到x点的最短路。

w[x]:x点为白色时,到x点的最短路。

这两个从刚才的推导中可以得到。因为一个点要被w[x]和b[x]都更新过才能使得最后能到终点(反图终点为1号节点)

而且此时边权为01,直接bfs去做。

因为是bfs,一个点先遍历到肯定比后来其他点遍历到这个点更优,所以一个点的w[x]被更新过了,对应的这个点的w[x]就不更新了,b[x]亦然。

而且因为一个点会被0和1更新两回,所以不能用vis[]来判在0和1的b[]/w[]中是否被更新过,因为这样会使得更新过就不更新了。

那么只用这两个转移可以嘛?

比如我用b[x]=min(b[x],b[y]+1/w[y]+1);   

这个b[x]开始初始化为0x3f3f3f3f.b[x]成为b[y]+1和w[y]+1中小的那个。

x--->z的时候用b[x]+1和w[x]+1更小的那个更新b[z],w[z];

但是实际上不对。题目使得最短路更大,这里节点x的选择应该选择b[y]和w[y]中更大的那个来更新,这样保证更新出来的最短路是更大的。所以还需要一个dp[x]。

设置dp[x]:x为任意色,到达x点时的最短路;

转移为:

b[y]=min(b[y],dp[x]+1);

w[y]=min(w[y],dp[x]+1);

dp[y]=max(w[y],b[y]);

dp[1]最后是否为无穷来判能否到1点。如果w[x]>=b[x]--->输出1,不然就0。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=5e5+100;
typedef long long LL;
LL w[maxn],b[maxn],dp[maxn],n,m;
struct P{
	LL to,col;
};
vector<P>g[maxn];
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  cin>>n>>m;
  for(LL i=1;i<=m;i++){
  	LL x,y,c;cin>>x>>y>>c;
  	g[y].push_back({x,c});
  }
  memset(w,0x3f,sizeof(w));
  memset(b,0x3f,sizeof(b));
  memset(dp,0x3f,sizeof(dp));
  w[n]=b[n]=dp[n]=0;
  queue<LL>q;
  q.push(n);
  while(!q.empty())
  {
  	LL t=q.front();q.pop();
	for(LL i=0;i<g[t].size();i++){
		LL v=g[t][i].to;
		LL c=g[t][i].col;
		if(c==0)
		{
			if(b[v]<=n) continue;
			b[v]=min(b[v],dp[t]+1);
			if(max(b[v],w[v])<=n){
				dp[v]=max(b[v],w[v]); 
				q.push(v); 	
			}
		}
		else{
			if(w[v]<=n) continue;
			w[v]=min(w[v],dp[t]+1);
			if(max(b[v],w[v])<=n){
				dp[v]=max(b[v],w[v]);
				q.push(v);	
			}
		}
	}	
  } 
  if(dp[1]>n) 
  {
  	cout<<"-1"<<endl;
  	for(LL i=1;i<=n;i++){
  		if(w[i]>=b[i]) cout<<1;
		else cout<<0;	
	}
  }
  else{
  	cout<<dp[1]<<endl;
  	for(LL i=1;i<=n;i++)
	{
  		if(w[i]>=b[i]) cout<<1;
		else cout<<0; 	
	}
	cout<<endl;
  }
return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/108585350