zoukankan      html  css  js  c++  java
  • 差分约束算法————洛谷P4878 [USACO05DEC] 布局

    题目:

     不难看出题意主要是给出ml+md个格式为xi-xj<=ak的不等式,xi-xj为i,j俩头牛的距离,要我们求x1-xn的最大值。

    经过上下加减我们可以将这几个不等式化成x1-xn<=a1+a2+a3+a4+....+ak,在这加减的过程中我们不难看到dijstra的身影,这加加减减的过程不正是松弛操作吗!

    这时我们就得到了正解——差分约束算法,此算法主要用于处理差分约束系统:如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统。

    结论:求解差分约束系统,都可以转化成图论的单源最短路径(或最长路径)问题。

    关于差分约束与最短路模型的关系

    我们观察上面例子中的不等式,都是x[i] - x[j] <= a[k],可以进行移项,成为x[i] <= x[j] + a[k],我们令a[k] = w(j, i),dis[i]=x[i],并使i=v,j=u,那么原始就变为:dis[u]+w(u,v)>=dis[v],于是可以联想到最短路模型中的一部分代码

    if(dis[u]+w(u,v)<=dis[v])
    {
        dis[v]=dis[u]+w(u,v);
    }

    这不正与松弛操作相似吗?

    但是好像不等号方向刚好相反,但其实这并不矛盾

    上面的代码要实现的是使dis[u]+w(u,v)>dis[v],而对于不等式,我们进行建边的操作:对于每个不等式 x[i] - x[j] <= a[k],对结点 j 和 i 建立一条 j -> i的有向边,边权为a[k],求x[n-1] - x[0] 的最大值就是求 0 到n-1的最短路,两者刚好吻合。所以求解差分约束问题就转化为了最短路问题。

    问题解的存在性

    由于在求解最短路时会出现存在负环或者终点根本不可达的情况,在求解差分约束问题时同样存在

     

    (1)存在负环

    如果路径中出现负环,就表示最短路可以无限小,即不存在最短路,那么在不等式上的表现即X[n-1] - X[0] <= T中的T无限小,得出的结论就是 X[n-1] - X[0]的最大值不存在。在SPFA实现过程中体现为某一点的入队次数大于节点数。(貌似可以用sqrt(num_node)来代替减少运行时间)

    (2)终点不可达

    这种情况表明X[n-1]和X[0]之间没有约束关系,X[n-1] - X[0]的最大值无限大,即X[n-1]和X[0]的取值有无限多种。在代码实现过程中体现为dis[n-1]=INF。

    参考的文章链接:https://blog.csdn.net/my_sunshine26/article/details/72849441


    注意

    1.因为本题中可能存在负权环(众所周知dijstra在碰到这个玩意时完全没有办法)所以我们需要用到SPFA

    2.后md个不等式题目一开始给的是:xj-xi>=a 我们可以推出xi-xj<=-a(这在之后的建图处理中会用到)注意负权边

    3.题目所给的条件不一定是对的,所以我们需要跑两次SPFA判断图是不是联通的。(因为洛谷上有3个坑逼数据)

    代码:

    此为没有考虑条件不正确的情况的代码,70分(洛谷上),在联赛是应该是100分的

    #include<bits/stdc++.h>
    using namespace std;
    #define INF 0x3f3f3f3f
    const int N=1005;
    const int M=40005;
    int n,ml,md;
    struct EDGE{
        int next,to,w;
    }edge[M];
    int head[N],tot;
    void add(int x,int y,int v){
        edge[++tot].next=head[x];
        edge[tot].to=y;
        edge[tot].w=v;
        head[x]=tot;
    }
    queue<int> q;
    int vis[N],dis[N],circle[N];//circle为指向tt的个数 
    void spfa(int s){
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        memset(circle,0,sizeof(circle));
        q.push(s);
        vis[s]=1,dis[s]=0;
        while(!q.empty()){
            int now=q.front(); 
    		q.pop(); 
    		vis[now]=0;
            for(int i=head[now];i;i=edge[i].next){
                int tt=edge[i].to;
                if(dis[now]+edge[i].w<dis[tt]){
                    dis[tt]=dis[now]+edge[i].w;
                    circle[tt]=circle[now]+1;
                    if(circle[tt]>=n){//指向tt的边超过n个自然是不满足条件的 
                        puts("-1");exit(0);
                    }
                    if(!vis[tt]){
                        vis[tt]=1;
                        q.push(tt);
                    }
                }
            }
        }
    
    }
    int main(){
        scanf("%d%d%d",&n,&ml,&md);
        for(int i=1;i<=ml;i++){
        	int a,b,d; 
            scanf("%d%d%d",&a,&b,&d);
            add(a,b,d);//a-b<=d
        }
        for(int i=1;i<=md;i++){
        	int a,b,d;
            scanf("%d%d%d",&a,&b,&d);
            add(b,a,-d);//b-a>=d ==> a-b<=-d
        }
        spfa(1);
        if(dis[n]>1e8){puts("-2");return 0;} 
        printf("%d",dis[n]);
        return 0;
    }
    

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    int n,ml,md,a,b,c,fst[10100],nex[50010],v[50010],w[50010],cnt,vis[10100],dis[10100],tim[10100];
    queue<int> q;
    void add(int a,int b,int c)
    {
        nex[++cnt]=fst[a];
        fst[a]=cnt;
        v[cnt]=b;
        w[cnt]=c;
        return ;
    }
    int spfa(int k)
    {
        memset(dis,0x7f/3,sizeof(dis));
        memset(vis,0,sizeof(vis));
        memset(tim,0,sizeof(tim));
        q.push(k);
        dis[k]=0;
        vis[k]=1;
        while(!q.empty())
        {
            int u=q.front();
            //cout<<u<<" ";
            q.pop();
            tim[u]++;
            vis[u]=0;
            if(tim[u]>n)
            return -1;
            for(int i=fst[u];i!=-1;i=nex[i])
            {
                if(dis[v[i]]>dis[u]+w[i])
                {
                    dis[v[i]]=dis[u]+w[i];
                    if(!vis[v[i]])
                    {
                        q.push(v[i]);
                        vis[v[i]]=1;
                    }
                }
            }
        }
        /*cout<<endl;
        for(int i=1;i<=n;i++)
        {
            cout<<dis[i]<<" ";
        }*/
        if(dis[n]>1e8)
        return -2;
        return dis[n];
    }
    int main()
    {
        memset(fst,-1,sizeof(fst));
        cin>>n>>ml>>md;
        for(int i=1;i<=ml;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
        }
        for(int i=1;i<=md;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            add(b,a,-c);
        }
        for(int i=1;i<n;i++)
        {
            add(i+1,i,0);
        }
        for(int i=1;i<=n;i++)
        {
            add(0,i,0);
        }
        int sp=spfa(0); 
        if(sp<=-1)
        {
            cout<<sp;
            return 0;
        }
        else
        {
            cout<<spfa(1);
        }
        //cout<<" "<<sp;
        return 0;
    }
    

      

    代码参考:https://www.luogu.org/blog/roy1994/solution-p4878  https://www.luogu.org/blog/mikasamikasa/solution-p4878

  • 相关阅读:
    jQuery prop方法
    ftp550权限问题
    一个很适合用来套用后台的框架
    Ajax中post方法400和404的问题
    图片上传,直接通过js预览
    JavaScript的DOM扩展
    DevExpress v16.1.5图表、Dashboard等多个控件API发生变化
    MyEclipse 2016正式版更新内容
    Smart Tag——DevExpress WPF初探
    MyEclipse使用心得:集成和使用Maven的方法
  • 原文地址:https://www.cnblogs.com/myhnb/p/11761542.html
Copyright © 2011-2022 走看看