• P3275 [SCOI2011]糖果


    题目描述

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

    输入格式

    ( 输入的第一行是两个整数 N,K。接下来 K 行,表示这些点需要满足的关系,每行 3 个数字,X,A,B。 )
    ( 如果 X=1, 表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多; )
    ( 如果 X=2, 表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果; )
    ( 如果 X=3, 表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果; )
    ( 如果 X=4, 表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果; )
    ( 如果 X=5, 表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果; )

    输出格式

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

    输入输出样例

    (输入 #1)

    5 7
    1 1 2
    2 3 2
    4 4 1
    3 4 5
    5 4 5
    2 3 5
    4 5 1
    

    (输出 #1)

    11
    

    说明/提示
    对于 (30%) 的数据,保证 (Nleq100)
    对于 (100%) 的数据,保证 (Nleq100000)
    对于所有的数据,保证 (Kleq100000), (1leq Xleq5), (1leq A), (Bleq N)

    思路

    首先这个题要用到差分约束。初学差分约束,应该要明确它的概念和它的用处。。。。。。。。。。。
    那么回到这个题上,因为要使总糖果数尽量少,所以大于或者小于之间只差1个最优,而可以取等时当然取等最优,那么存图就搞定了。因为不一定是哪一个人手中的糖果最少,所以设立一个0号节点,并到每一个人的边权都是1(每一个人手里都至少有一个糖果)。因为要满足所有的条件,也就是只要加1这个1必须加,不然不符合题意,所以用SPFA(bfs)跑一遍单源最长路(从0号节点开始),就是每个人满足条件是的最少持有的糖果(最少持有体现在存图中的1和0),再将所有的最长路加起来即为最终答案。

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<cstring>
    using namespace std;
    typedef long long int ll;
    const int N=100005,M=300005;
    ll tot[N],ver[M],Next[M],head[N],d[N],edge[M];
    bool v[N];
    ll ans,n,k,cnt;
    queue<ll> q;
    void add(ll x,ll y,ll z){
    	ver[++cnt]=y;
    	edge[cnt]=z;
    	Next[cnt]=head[x];
    	head[x]=cnt; 
    } 
    int main()
    {
    	scanf("%lld%lld",&n,&k);
    	for(ll i(1),op,u,v;i<=k;++i){
    		scanf("%lld%lld%lld",&op,&u,&v);
    		if(op==1){//u=v 
    			add(u,v,0);
    			add(v,u,0);//糖果数相等,那么相差0,所以边权为0 
    		}
    		else if(op==2){//v>u
    			if(u==v){
    				printf("-1
    ");//同一个人的糖果数肯定相等,所以如果一个大一个小肯定不对,直接输出不行 
    				return 0;
    			}
    			add(u,v,1);//贪心思想,要使糖果数最少,v比u多一个糖果才满足题意 
    		}
    		else if(op==3){//u<=v
    			add(v,u,0);//大于等于和小于等于都取等才符合贪心思想 
    		}
    		else if(op==4){//u>v
    			if(u==v){
    				printf("-1
    ");
    				return 0;
    			}
    			add(v,u,1);
    		}
    		else if(op==5){//v<=u
    			add(u,v,0);
    		}
    	}
    	for(ll i=n;i>=1;i--){
    		add(0,i,1);//初始一个0节点,因为不一定哪一个人糖果最少,所以不一定从哪个人开始传递关系。而每个人手里都至少有一个糖果,所以相差1 
    	} 
    	v[0]=1;
    	q.push(0);
    	while(!q.empty()){//SPFA
    		ll x=q.front();
    		q.pop();
    		if(tot[x]==n-1){//判定环,因为如果有环的话说明大小关系出现了矛盾,而SPFA判断环的方法就是看是否入队了n-1次 
    			printf("-1
    ");//有环那么不行 
    			return 0;
    		}
    		v[x]=0;
    		tot[x]++;//入队次数 
    		for(ll i=head[x];i;i=Next[i]){
    			ll y=ver[i],z=edge[i];
    			if(d[y]<d[x]+z){//SPFA求最长路,因为要满足所有条件所以是最长路 
    				d[y]=d[x]+z;
    				if(!v[y]){
    					q.push(y);
    					v[y]=1;
    				}
    			}
    		}
    	}
    	for(ll i=1;i<=n;i++){
    		ans+=d[i];//每一个人至少拿着的糖果 
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    但是,请注意,由于SPFA(bfs)的时间复杂度最坏是O(nm),这个题数据范围较大,按理来说会TLE。中间有一个玄学操作,就是我在存0号节点的时候,是倒着循环的,我也不知道为什么,或许是存边顺序会影响SPFA的时间复杂度。管他呢,反正这题正解不是差分约束。。。就当做一个板子题吧。

  • 相关阅读:
    Redhat各个版本和内核对照
    Java8 lambda表达式总结
    conda 安装指定版本的指定包
    git初始化的几句shell
    MYsqli 绑定插入与查询实例
    按天去除重复数据,为0则取0,否则取最大的那个值
    存储过程,循环插入1000条记录
    主表如何统计在附表中的出现次数?
    Invalid argument supplied for foreach()
    二十、mysql mysqldump备份工具
  • 原文地址:https://www.cnblogs.com/57xmz/p/13442209.html
Copyright © 2020-2023  润新知