题目描述
幼儿园里有 \(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%\) 的数据,保证 \(N\leq100\)
对于 \(100%\) 的数据,保证 \(N\leq100000\)
对于所有的数据,保证 \(K\leq100000\), \(1\leq X\leq5\), \(1\leq A\), \(B\leq N\)
思路
首先这个题要用到差分约束。初学差分约束,应该要明确它的概念和它的用处。。。。。。。。。。。
那么回到这个题上,因为要使总糖果数尽量少,所以大于或者小于之间只差1个最优,而可以取等时当然取等最优,那么存图就搞定了。因为不一定是哪一个人手中的糖果最少,所以设立一个0号节点,并到每一个人的边权都是1(每一个人手里都至少有一个糖果)。因为要满足所有的条件,也就是只要加1这个1必须加,不然不符合题意,所以用SPFA(bfs)跑一遍单源最长路(从0号节点开始),就是每个人满足条件是的最少持有的糖果(最少持有体现在存图中的1和0),再将所有的最长路加起来即为最终答案。
代码
#include
#include
#include
#include
#include
#include
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 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\n");//同一个人的糖果数肯定相等,所以如果一个大一个小肯定不对,直接输出不行
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\n");
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\n");//有环那么不行
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]
但是,请注意,由于SPFA(bfs)的时间复杂度最坏是O(nm),这个题数据范围较大,按理来说会TLE。中间有一个玄学操作,就是我在存0号节点的时候,是倒着循环的,我也不知道为什么,或许是存边顺序会影响SPFA的时间复杂度。管他呢,反正这题正解不是差分约束。。。就当做一个板子题吧。