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
#include
#include
#include
#include
#include
#include
#include
#include
#define debug(a) cout<<#a<<"="<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;
  queueq;
  q.push(n);
  while(!q.empty())
  {
  	LL t=q.front();q.pop();
	for(LL i=0;in) 
  {
  	cout<<"-1"<=b[i]) cout<<1;
		else cout<<0;	
	}
  }
  else{
  	cout<=b[i]) cout<<1;
		else cout<<0; 	
	}
	cout<

 

你可能感兴趣的:(线性dp,最短路)