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的时间复杂度。管他呢,反正这题正解不是差分约束。。。就当做一个板子题吧。

原文地址:https://www.cnblogs.com/57xmz/p/13442209.html