codeforces 241 E Flights

题目大意

一个有向图,规定每条边的边权只能为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值