• 洛谷P3275 [SCOI2011]糖果(spfa(松弛))


    题目地址https://www.luogu.com.cn/problem/P3275

    题目描述

    幼儿园里有 N 个小朋友, 老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,需要满足小朋友们的 KK 个要求。幼儿园的糖果总是有限的,想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。

    输入

    输入的第一行是两个整数 NK。接下来 K 行,表示这些点需要满足的关系,每行 33 个数字,X,A,B。

    • 如果 X=1, 表示第 A个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多;
    • 如果 X=2, 表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果;
    • 如果 X=3, 表示第 A个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果;
    • 如果 X=4, 表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果;
    • 如果 X=5, 表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果;

    输出

    输出一行,表示 老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 1。

    题解:对于这种只给出两个结点之间的关系的话,可以使用spfa的松弛操作。x=1,其实就是a<=b&&a>=b,所以在a,b之间需要加两条有向边,分别是a到b和b到a,并且权值为0;x=2,在a到b加一条有向边,权值为1;x=3,表示a>=b,在b到a之间加一条权值为0的有向边;x=4,表示a>b,在b到a之间加一条权值为1的有向边;x=5,表示a<=b,在a到b之间加一条权值为0的有向边。另外,需要满足每个点的权值大于等于1,所以需要一个超级源点s(可以为结点0),加n条s到i(i从1到n)权值为1的有向边。

    这个时候就可以使用松弛操作,找到到结点i的路径上最长路。只要可以理解这个最长路就可以了。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define P pair<int,int>
    const int N=1e5+10;
    const int M=1e5+10;
    struct st{
        int from,to,dis,next;
    }edge[M*3];
    int head[N],cnt=0,n,k,dis[N],num[N],flag=0;
    void addedge(int u,int v,int w){
        cnt++;
        edge[cnt].from=u;
        edge[cnt].to=v;
        edge[cnt].dis=w;
        edge[cnt].next=head[u];
        head[u]=cnt;
        return ;
    }
    void spfa(int s){
        if(flag==1) return ;
        int vis[N]={0};
        memset(dis,0,sizeof(dis));
        memset(num,0,sizeof(num));
        queue<int>q;
        q.push(s);
        dis[s]=0;
        vis[s]=1;
        while(!q.empty()){
            int pa=q.front();q.pop();
            vis[pa]=0;
            num[pa]++;
            if(num[pa]==n){
                flag=1;
                return ;
            }
            for(int i=head[pa];i!=-1;i=edge[i].next){
                int c=edge[i].to,d=edge[i].dis;
                if(dis[c]<dis[pa]+d){
                    dis[c]=dis[pa]+d;
                    if(vis[c]==0){
                        vis[c]=1;
                        q.push(c);
                    }
                }
            }
        }
    }
    
    int main(){
        cin>>n>>k;
        flag=0;
        memset(head,-1,sizeof(head));
        for(int i=1,u,v,x;i<=k;i++){
            scanf("%d%d%d",&x,&u,&v);
            if((x==2||x==4)&&u==v) flag=1; 
            switch(x){
                case 1:addedge(u,v,0),addedge(v,u,0);break;
                case 2:addedge(u,v,1);break;
                case 3:addedge(v,u,0);break;
                case 4:addedge(v,u,1);break;
                case 5:addedge(u,v,0);break;
            }
        }
        for(int i=n;i;i--){
            addedge(0,i,1);
        }
        if(flag==1){
            cout<<"-1";
            return 0;
        }
        spfa(0);
        long long int ans=0;
        for(int i=1;i<=n;i++)    ans+=dis[i];
        if(flag==1) cout<<"-1";
        else cout<<ans<<endl;
        return 0; 
    }
    
    /*
    9 8
    2 1 2
    2 2 3
    2 3 4
    2 4 5
    4 6 5
    2 6 7
    2 7 8
    2 8 9
    */
    /*
    6 5
    2 1 2
    2 2 3
    2 3 4
    2 4 5
    4 6 5
    */

    注:不过这里有个超级坑的测试点,如果使用的链式前向星,那么假如超级源点的n条有向边时,必须从n到1添加;如果使用的是vector,那么添加超级源点的n条有向边时,必须从1到n添加。否则会超时,会被卡,在那个超级气人的测试点中,spfa会成为O(VE)的复杂度,简直是超级坑。这个测试点的数据如代码下方我给出的这样的格式,不过测试点中的n,k非常大。所以可以不使用spfa就尽量不使用spfa,毕竟这个算法的复杂度并不稳定。但是对于需要松弛操作的题而言,spfa又是比较简单的方法。

    写于:2020/8/14 22:51


    作者:孙建钊
    出处:http://www.cnblogs.com/sunjianzhao/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    mysql查找有某列但没有此列索引的表
    mysql找到所有索引
    mysq在某一刻同时获取主从库的位置点
    新书《深入应用C++11:代码优化与工程级应用》出版,感谢支持
    c++11实现一个简单的lexical_cast
    应该用bind+function取代虚函数吗?
    《深入应用C++11:代码优化与工程级应用》开始发售
    一个更好的C++序列化/反序列化库Kapok
    C++技术沙龙主要内容
    C++11模版元编程
  • 原文地址:https://www.cnblogs.com/sunjianzhao/p/13506061.html
Copyright © 2020-2023  润新知