糖果-差分约束

糖果-差分约束

题目描述

题目描述

题解

典型的差分约束;
我们不妨假设边 u → v u→v uv表示的是 v v v u u u大多少,贪心的想到要使得最后的糖果数最小,就尽可能的使得相连的两点糖果数差值尽可能的小(一定是以两者间小的为标准,相等时差为 0 0 0,否则大的数比小的至少大 1 1 1),最后的糖果总数显然最大。

于是我们针对这5种情况分别建边(以下出现的 s i z [ x ] siz[x] siz[x]表示的是 x x x的糖果数):
  1、当条件为 s i z [ u ] = = s i z [ v ] siz[u]==siz[v] siz[u]==siz[v],则建边 w [ u , v ] = 0 ; w [ v , u ] = 0 w[u,v]=0;w[v,u]=0 w[u,v]=0;w[v,u]=0表示 s i z [ u ] = = s i z [ v ] siz[u]==siz[v] siz[u]==siz[v]
  2、当条件为 s i z [ u ] < s i z [ v ] siz[u]<siz[v] siz[u]<siz[v],若 u = = v u==v u==v则直接输出 − 1 −1 1(显然不成立),否则建边 w [ u , v ] = 1 w[u,v]=1 w[u,v]=1(表示 s i z [ v ] 比 s i z [ u ] 大 1 siz[v]比siz[u]大1 siz[v]siz[u]1
  3、当条件为 s i z [ u ] > = s i z [ v ] siz[u]>=siz[v] siz[u]>=siz[v],则建边 w [ v , u ] = 0 w[v,u]=0 w[v,u]=0(表示 s i z [ u ] = = s i z [ v ] siz[u]==siz[v] siz[u]==siz[v],注意方向 v → u v→u vu,因为要保证最优性,就必须从小的向大的转移,尽可能的让大的和小的相等)
  4、当条件为 s i z [ u ] > s i z [ v ] siz[u]>siz[v] siz[u]>siz[v],若 u = = v u==v u==v则直接输出 − 1 −1 1(显然不成立),否则建边 w [ v , u ] = 1 w[v,u]=1 w[v,u]=1(表示 s i z [ u ] 比 s i z [ v ] 大 1 siz[u]比siz[v]大1 siz[u]siz[v]1
  5、当条件为 s i z [ u ] < = s i z [ v ] siz[u]<=siz[v] siz[u]<=siz[v],则建边 w [ u , v ] = 0 w[u,v]=0 w[u,v]=0(表示 s i z [ v ] = = s i z [ u ] siz[v]==siz[u] siz[v]==siz[u],注意方向 u → v u→v uv,因为要保证最优性,就必须从小的向大的转移,尽可能的让大的和小的相等
  接着,新建一个 0 0 0节点作为源点,向 i = 1   n i=1~n i=1 n所有点都连边 w [ 0 , i ] = 1 w[0,i]=1 w[0,i]=1(表示每个点至少有1个糖果)(倒序建边,否则 s p f a spfa spfa T T T)
  然后,我们跑一遍最长路

代码实现

#include<bits/stdc++.h>//差分约束 
#define M 200009
using namespace std;
int nxt[M],first[M],w[M],to[M],tot;
int n,m,vis[M],cnt[M];
long long ans,d[M];
inline int read(){
	int f=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f; 
}
inline void add(int x,int y,int z){
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
inline void spfa(){
	vis[0]=1,d[0]=0;
	queue<int>q;
	q.push(0);
	while(q.size()){
		int u=q.front();
		q.pop(),vis[u]=0;
		if(cnt[u]==n-1){printf("-1\n"),exit(0);}
		cnt[u]++;
		for(register int i=first[u];i;i=nxt[i]){
			int v=to[i];
			if(d[v]<d[u]+w[i]){
				d[v]=d[u]+w[i];
				if(!vis[v]) q.push(v),vis[v]=1;
			}
		}
	}
}
int main(){
	int type,x,y;
	n=read(),m=read();
	for(register int i=1;i<=m;i++){
		type=read(),x=read(),y=read();
		if(type%2==0&&x==y) return !printf("-1\n");
		if(type==1) add(x,y,0),add(y,x,0);
		if(type==2) add(x,y,1);
		if(type==3) add(y,x,0);
		if(type==4)	add(y,x,1);
		if(type==5) add(x,y,0);
	}for(register int i=n;i>=1;i--) add(0,i,1);
	spfa();
	for(register int i=1;i<=n;i++) ans+=d[i];
	printf("%lld\n",ans);
	return 0; 
}

做题启发

1,对于卡spfa的题,可以考虑倒序建边。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值