差分约束总结

差分约束,常见的板子类题目,但又不太板子,稍微总结一下:

(1.)常见的差分约束,将不等式转化为(SPFA)的松弛操作,注意最短路与最长路的区分即可。

(Eg:[USACO05DEC])布局

较为板子的板子,如果有(x_i-x_jleq d),建边(j)(i),边权为(d),跑最短路,这是我们最开始的想法,但是这一题告诉我们了一个重要的事情,我们要从(0)(x_i)建一条边权为(0)的边,先从(0)跑一遍(SPFA),再从(1)跑一遍,因为我们不能保证图的连通性……

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int N=30010;
int n,Ml,Md,num_edge,ans;
int head[N<<1],Cnt[N],dis[N],Vis[N];
struct Edge{int next,dis,to;} edge[N<<1];
inline void Add(int from,int to,int dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].dis=dis;
    edge[num_edge].to=to;
    head[from]=num_edge;
}
inline void Spfa(int S)
{
    memset(Cnt,0,sizeof(Cnt));
    memset(Vis,0,sizeof(Vis));
    memset(dis,0x3f3f3f3f,sizeof(dis));
    queue<int> q;q.push(S);dis[S]=0;Vis[S]=1;
    while(!q.empty())
    {
		int x=q.front();q.pop();Vis[x]=0;
		for(int i=head[x];i;i=edge[i].next)
	    	if(dis[edge[i].to]>dis[x]+edge[i].dis)
	    	{
				dis[edge[i].to]=dis[x]+edge[i].dis;
				Cnt[edge[i].to]=Cnt[x]+1;
				if(Cnt[edge[i].to]>=n) {puts("-1");exit(0);}
				if(!Vis[edge[i].to]) q.push(edge[i].to),Vis[edge[i].to]=1;
	    	}   
    }
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
#endif
    n=read();Ml=read();Md=read();
    for(int i=1;i<=n;i++) Add(0,i,0);
    for(int i=1,u,v,d;i<=Ml;i++)
		u=read(),v=read(),d=read(),Add(u,v,d);
    for(int i=1,u,v,d;i<=Md;i++)
		u=read(),v=read(),d=read(),Add(v,u,-d);
    Spfa(0),Spfa(1);
    printf("%d",(dis[n]==0x3f3f3f3f?-2:dis[n]));
}

(2.)特殊类型的差分约束

(Eg:[SCOI2008])天平

我们设(Max[i][j])表示(i,j)的最大可能差值,(Min[i][j])表示(i,j)的最小可能差值,直接跑(Floyd)即可,这类题的特殊之处在于其设的不同,并不是单纯地跑最短路。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int N=70;
int n,A,B,num_edge,Max[N][N],Min[N][N],ans1,ans2,ans3;
int main(){
#ifndef ONLINE_JUDGE
    //freopen("1.in","r",stdin);//Ans=1 4 1;
    freopen("2.in","r",stdin);//Ans=18 12 11;
#endif
    n=read(),A=read(),B=read();
    for(int i=1;i<=n;i++)
    {
		char s[N];scanf("%s",s);
		for(int j=0;j<n;j++)
	    	if(s[j]=='='||i==j+1) Max[i][j+1]=Min[i][j+1]=0;
	    	else if(s[j]=='+') Max[i][j+1]=2,Min[i][j+1]=1;
	    	else if(s[j]=='-') Max[i][j+1]=-1,Min[i][j+1]=-2;
	    	else Max[i][j+1]=2,Min[i][j+1]=-2;
    }
    for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
	    if(i!=k)
			for(int j=1;j<=n;j++)
		    	if(j!=k&&i!=j)
		    	{
					Max[i][j]=min(Max[i][j],Max[i][k]+Max[k][j]);
					Min[i][j]=max(Min[i][j],Min[i][k]+Min[k][j]);
		    	}
    for(int C=1;C<=n;C++)
		if(A!=C&&B!=C)
	    	for(int D=1;D<C;D++)
			if(A!=D&&B!=D)
			{
		    	ans1+=(Min[A][C]>Max[D][B])||(Min[A][D]>Max[C][B]);
		    	ans3+=(Min[C][A]>Max[B][D])||(Min[D][A]>Max[B][C]);
		    	ans2+=((Min[A][C]==Max[A][C])&&(Min[D][B]==Max[D][B])&&(Min[A][C]==Min[D][B]))||((Min[A][D]==Max[A][D])&&(Min[C][B]==Max[C][B])&&(Min[A][D]==Min[C][B]));
			}
    printf("%d %d %d",ans1,ans2,ans3);
}

(Eg:[1007])倍杀测量者

这一题题意需要简化后才能出来思路,先看到找答案,显然二分,然后考虑判断答案是否合法。

显然我们要把图重建一遍,但是所谓的“女装(Flag)“该怎么办?考虑一个人在什么情况下要女装,当(i)立了一个(Flag)曰:“我没(k)倍杀选手(j),我就女装时”,如果他不满足(x_i>x_j*k)时就会被强迫女装。

同理,当(i)立了”选手Y把我kkk倍杀,我就女装“,如果他不满足(x_i>x_j*frac{1}{k})时就会被强迫女装。

对于选手(i)固定的分数(x),我们建边(0)(i),边权为(x),建边(i)(0),边权为(frac{1}{x})

此时我们将最短路求和转化为最短路求积即可,如果要取模,就取(log),但这一题并不要。(我记得某个毒瘤学长就出了这样的一道题……)

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int N=30010;
const double Eps=1e-8;
double dis[N],X[N];
int n,Fls,Cos,num_edge;
int head[N<<1],Cnt[N],Vis[N],C[N];
struct Flag{int typ,A,B;double k;} Oier[N];
struct Edge{int next,to;double dis;} edge[N<<1];
inline void Add(int from,int to,double dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].dis=dis;
    edge[num_edge].to=to;
    head[from]=num_edge;
}
inline bool Spfa(int S)
{
    memset(Cnt,0,sizeof(Cnt));
    memset(Vis,0,sizeof(Vis));
    memset(dis,0,sizeof(dis));
    queue<int> q;q.push(S);Vis[S]=1;dis[S]=1;
    while(!q.empty())
    {
	int x=q.front();q.pop();Vis[x]=0;
	for(int i=head[x];i;i=edge[i].next)
	    if(dis[edge[i].to]<dis[x]*edge[i].dis)
	    {
		dis[edge[i].to]=dis[x]*edge[i].dis;
		if(!Vis[edge[i].to])
		{
		    q.push(edge[i].to),Vis[edge[i].to]=1;
		    Cnt[edge[i].to]=Cnt[x]+1;
		    if(Cnt[edge[i].to]>n+1) return 1;
		}
	    }
    }
    return 0;
}
inline bool Check(double T)
{
    num_edge=0;memset(head,0,sizeof(head));
    for(int i=1;i<=Cos;i++)
	Add(0,C[i],X[i]),Add(C[i],0,1.0/X[i]);
    for(int i=1;i<=Fls;i++)
	if(Oier[i].typ==1) Add(Oier[i].B,Oier[i].A,Oier[i].k-T);
	else Add(Oier[i].B,Oier[i].A,1.0/(Oier[i].k+T));
    return Spfa(0);
}
int main(){
#ifndef ONLINE_JUDGE
    //freopen("1.in","r",stdin);//Ans=-1;
    freopen("2.in","r",stdin);//Ans=3.9999993984;
#endif
    double l=0,r=0,ans=-1;
    n=read();Fls=read();Cos=read();
    for(int i=1;i<=Fls;i++)
    {
	Oier[i].typ=read(),Oier[i].A=read();
	Oier[i].B=read(),Oier[i].k=(double)read();
	r=max(Oier[i].k,r);
    }
    //cout<<l<<' '<<r;
    for(int i=1;i<=Cos;i++) C[i]=read(),X[i]=(double)read();
    while(r-l>Eps)
    {
	double mid=(l+r)/2;
	if(Check(mid)) l=ans=mid;
	else r=mid;
    }
    ans==-1?puts("-1"):printf("%.10lf
",ans);
}
原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11412611.html