题目大意
一个有向图,规定每条边的边权只能为1或2,要求给出一种方案,使得每条1到n的路径长度都一样。
题解
差分约束系统。
如果一条边不在1到n的路径上,那么就不用考虑它。
于是可以建出一个新图。
对于一条边
(a,b)
,则有
dis(b)−dis(a)⩽2
dis(a)−dib(b)⩽−1
然后跑一边SPFA,判掉负圈,有就是无解。
然后遍历所有的边,注意,只有在1~n的路径上的边,边权大于2或小于1时,才是无解,其他的边可以任意赋值,因为它们对答案无影响。
至于如何构图,我一开始是想dp一下,记录每个点是否可以到n。
我用-1表示未访问,1表示可以,0表示不可以
由于一些点不一定可以到达,所以这些点的dp值始终是-1,然而我在判断是不是在新图中时用的是
dp[i]!=0
,于是挂掉了,WA了好几次。
当然还可以两边dfs,从起点dfs一遍,终点在反向图上dfs一遍,两个点都可以到达的点才是新图上的点。
贴代码:
两边dfs:
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int M=1005;
struct Edge{
int to,v,nxt;
Edge(int _to=0,int _v=0,int _nxt=0):to(_to),v(_v),nxt(_nxt){}
}edge[M*10];
int etot,n,m,a[M*10],b[M*10];
queue<int>Q;
int cnt[M],dis[M],head[M];
bool mark[M],flag[M];
void add_edge(int a,int b,int c){
edge[etot]=Edge(b,c,head[a]);
head[a]=etot++;
}
int on_the_road[M];
bool SPFA(){
for(int i=2;i<=n;i++)
dis[i]=2000000000;
Q.push(1);
while(!Q.empty()){
int x=Q.front();
Q.pop();
mark[x]=0;
for(int i=head[x];~i;i=edge[i].nxt){
int to=edge[i].to;
if(on_the_road[to]!=2) continue;
if(dis[to]>dis[x]+edge[i].v){
dis[to]=dis[x]+edge[i].v;
if(++cnt[to]>n) return false;
if(!mark[to]){
mark[to]=1;
Q.push(to);
}
}
}
}
return true;
}
void dfs(int x,bool f){
if(flag[x]) return;
flag[x]=1;
on_the_road[x]++;
for(int i=head[x];~i;i=edge[i].nxt)
if((f==1)^(edge[i].v>0)) dfs(edge[i].to,f);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
head[i]=-1;
for(int i=1;i<=m;i++){
scanf("%d%d",&a[i],&b[i]);
add_edge(a[i],b[i],2);
add_edge(b[i],a[i],-1);
}
dfs(1,0);
for(int i=1;i<=n;i++)
flag[i]=0;
dfs(n,1);
if(!SPFA()) puts("No");
else{
for(int j=1;j<=m;j++){
if(on_the_road[b[j]]==2&&on_the_road[a[j]]==2&&(dis[b[j]]-dis[a[j]]<=0||dis[b[j]]-dis[a[j]]>2)){
puts("No");
return 0;
}
}
puts("Yes");
for(int i=1;i<=m;i++){
if(dis[b[i]]-dis[a[i]]<1||dis[b[i]]-dis[a[i]]>2) puts("1");
else printf("%d\n",dis[b[i]]-dis[a[i]]);
}
}
return 0;
}
dp
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int M=1005;
struct Edge{
int to,v,nxt;
Edge(int _to=0,int _v=0,int _nxt=0):to(_to),v(_v),nxt(_nxt){}
}edge[M*10];
int etot,n,m,a[M*10],b[M*10];
queue<int>Q;
int cnt[M],dis[M],head[M];
bool mark[M];
void add_edge(int a,int b,int c){
edge[etot]=Edge(b,c,head[a]);
head[a]=etot++;
}
int on_the_road[M];
bool SPFA(){
for(int i=2;i<=n;i++)
dis[i]=2000000000;
Q.push(1);
while(!Q.empty()){
int x=Q.front();
Q.pop();
mark[x]=0;
for(int i=head[x];~i;i=edge[i].nxt){
int to=edge[i].to;
if(!on_the_road[to]||on_the_road[to]==-1) continue;
if(dis[to]>dis[x]+edge[i].v){
dis[to]=dis[x]+edge[i].v;
if(++cnt[to]>n) return false;
if(!mark[to]){
mark[to]=1;
Q.push(to);
}
}
}
}
return true;
}
int dfs(int x){
if(on_the_road[x]!=-1) return on_the_road[x];
on_the_road[x]=0;
for(int i=head[x];~i;i=edge[i].nxt)
if(edge[i].v>0&&dfs(edge[i].to)) on_the_road[x]=1;
if(x==n) on_the_road[x]=1;
return on_the_road[x];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
on_the_road[i]=head[i]=-1;
for(int i=1;i<=m;i++){
scanf("%d%d",&a[i],&b[i]);
add_edge(a[i],b[i],2);
add_edge(b[i],a[i],-1);
}
dfs(1);
if(!SPFA()) puts("No");
else{
for(int j=1;j<=m;j++){
if(on_the_road[b[j]]>0&&on_the_road[a[j]]>0&&(dis[b[j]]-dis[a[j]]<=0||dis[b[j]]-dis[a[j]]>2)){
puts("No");
return 0;
}
}
puts("Yes");
for(int i=1;i<=m;i++){
if(dis[b[i]]-dis[a[i]]<1||dis[b[i]]-dis[a[i]]>2) puts("1");
else printf("%d\n",dis[b[i]]-dis[a[i]]);
}
}
return 0;
}