题目描述
幼儿园里有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。
样例输入
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
样例输出
11
题解
今天学的是差分约束的问题,听了同学说差分是需要最短路径之后我就回去看了最短路径,以前只会Dijkstra算法,前几天学了链式前向星之后就自然而然去看了spfa算法,然后等spfa掌握之后差分约束的影子也就差不多出来了。我的理解是差分约束就是根据题目所给的约束条件来建有向边,然后题目如果求最大值就求单源最短路径,如果求最小值就求最长路径,具体怎么建立边还要跟据题目要求。
case1的情况就是a=b,用管理运筹学的方法把它拆分为a>=b和a<=b,我们就在b->a和a->b之间各建一条权值为0的有向边;
case2的情况就是a<b,那么我们就在建一条a->b的有向边,由于a必须小于b,而题目要求的是最少需要的糖果,那么b至少要比a大1,所以这条边权值为1,然后如果当输入的a=b时,就与a<b冲突,此时整个方程无解;
case3的情况是a>=b,我们建一条b->a的有向边,由于a可以等于b,所以权值为0;
case4的情况是a>b,同理建一条b->a权值为1的有向边,且a=b时整道题无解;
case5的情况是a<=b,同理建一条a->b权值为0的有向边。
然后我们设一个0点,给0到每一个小朋友赋一条权值为 1的有向边,保证每个小朋友最少有一颗糖,然后就像单源最短路径一样进入spfa函数,由于题目求的是最小值,所以我们要跑从0点到每一个点的最长路径:在dist[v]<dist[u]+w的时候更新dist值。最终得到的dist数组的值就是每个小朋友最少需要的糖果数目,求和之后就是最终需要的糖果总数。
#include<iostream>
#include<queue>
#include<cstdio>
using namespace std;
#define ll long long
const int maxn=2e5+10;
int n,m,tot=0,head[maxn],dist[maxn],vis[maxn],in[maxn];
ll ans=0;
struct Edge
{
int to,weight,next;
}edge[maxn*2];
void add_edge(int from,int to,int w)
{
edge[++tot].to=to;
edge[tot].next=head[from];
edge[tot].weight=w;
head[from]=tot;
}
bool spfa()
{
queue<int>q;
q.push(0);
vis[0]=1;
dist[0]=0;
in[0]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to,w=edge[i].weight;
if(dist[v]<dist[u]+w)//求最小值跑最长路径
{
dist[v]=dist[u]+w;
in[v]++;
if(in[v]>=n)
return 0;
if(!vis[v])//如果v没有入队
{
q.push(v);
vis[v]=1;
}
}
}
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
while(m--)
{
int a,b,x;
scanf("%d%d%d",&x,&a,&b);
switch(x)
{
//a=b转换为a<=b;a>=b即a->b、b->a至少需要0
case 1:
add_edge(a,b,0);
add_edge(b,a,0);
break;
//a<b即a->b至少需要1
case 2:
if(a==b)//如果a=b则与a->b至少需要1矛盾,无解
{
printf("-1");
return 0;
}
add_edge(a,b,1);
break;
//a>=b即b->a至少需要0
case 3:
add_edge(b,a,0);
break;
//a>b即b->a至少需要1
case 4:
if(a==b)//如果a=b则与b->a至少需要1矛盾,无解
{
printf("-1");
return 0;
}
add_edge(b,a,1);
break;
//a<=b即a->b至少需要0
case 5:
add_edge(a,b,0);
break;
}
}
for(int i=n;i>0;i--)
add_edge(0,i,1);//每个小朋友至少一颗
if(!spfa())
{
printf("-1");
return 0;
}
for(int i=1;i<=n;i++)
ans+=dist[i];
printf("%lld",ans);
return 0;
}