• 差分约束


    P3275 [SCOI2011]糖果

    差分约束的意味很浓了

    差分约束就是 \(a-b\ge c\) 转化为 \(addedge(b,a,c)\) 然后跑最短路,判断是否有负环。有负环表示无解。

    这个用三角形的基础知识可以证明。

    这题直接按照上面的方法建边即可。可以建一个“超级起点”来简化题目并且保证了图的连通性。

    如果求最大值,那么按小于等于建图后求最短路即可。如果求最小值,那么按小于等于建图后求最长路即可。

    这题就直接跑最短路。但是这题卡掉了深搜版的差分约束。两种方法最好都了解一下。

    广搜

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100010;
    #define rint register int
    int n,m,num_edge,head[N],dist[N],cnt[N];
    long long ans;
    bool inq[N];
    struct edge{
    	int to,nxt,val;
    }e[N*3];
    inline void add(int from,int to,int val)
    {
    	++num_edge;
    	e[num_edge].nxt=head[from];
    	e[num_edge].to=to;
    	e[num_edge].val=val;
    	head[from]=num_edge; 
    }
    inline bool spfa()
    {
    	queue<int>q;
    	q.push(0);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		++cnt[u];inq[u]=false;
    		if(cnt[u]>=n)return true;
    		for(int i=head[u];i;i=e[i].nxt)
    		{
    			int v=e[i].to;
    			if(dist[v]<dist[u]+e[i].val)
    			{
    				dist[v]=dist[u]+e[i].val;
    				if(!inq[v]){q.push(v);inq[v]=true;}
    			}
    		}
    	}
    	return false;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(rint i=1,op,a,b;i<=m;++i)
    	{
    		scanf("%d%d%d",&op,&a,&b);
    		if(op==1){add(b,a,0);add(a,b,0);}
    		if(op==2){add(a,b,1);if(a==b){puts("-1");return 0;}}
    		if(op==3){add(b,a,0);}
    		if(op==4){add(b,a,1);if(a==b){puts("-1");return 0;}}
    		if(op==5){add(a,b,0);} 
    	}
    	for(rint i=n;i>=1;--i)add(0,i,1);
    	if(spfa()){puts("-1");return 0;}
    	for(rint i=1;i<=n;++i)ans+=dist[i];
    	printf("%lld\n",ans);
    	return 0;
    }
    

    深搜

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100010;
    int n,m,num_edge,head[N],dist[N];
    long long ans;
    bool ins[N];
    struct edge{
    	int to,nxt,val;
    }e[N*3];
    void add(int from,int to,int val)
    {
    	++num_edge;
    	e[num_edge].nxt=head[from];
    	e[num_edge].to=to;
    	e[num_edge].val=val;
    	head[from]=num_edge; 
    }
    bool spfa(int u)
    {
    	ins[u]=true;
    	for(int i=head[u];i;i=e[i].nxt)
    	{
    		int v=e[i].to,w=e[i].val;
    		if(dist[v]<dist[u]+w)
    		{
    			dist[v]=dist[u]+w;
    			if(ins[v])return true;
    			if(spfa(v))return true;
    		}
    	}
    	ins[u]=false;
    	return false;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1,op,a,b;i<=m;++i)
    	{
    		scanf("%d%d%d",&op,&a,&b);
    		if(op==1){add(b,a,0);add(a,b,0);}
    		if(op==2){add(a,b,1);if(a==b){
    		}}
    		if(op==3){add(b,a,0);}
    		if(op==4){add(b,a,1);}
    		if(op==5){add(a,b,0);} 
    	}
    	for(int i=1;i<=n;++i)add(0,i,1);
    	for(int i=0;i<=n;++i)
    		ins[i]=false;
    	dist[0]=0;
    	if(spfa(0)){puts("-1");return 0;}
    	for(int i=1;i<=n;++i)ans+=dist[i];
    	printf("%d\n",ans);
    	return 0;
    }
    

    POJ1275 Cashier Employment

    差分约束的经典,如果很快能推出所有的约束条件,%%%

    发现这题如果知道用几个人就很好做了,因为约束条件总是不够,加上人数就可以差分约束了。而且人数还是有上下界的:[0,n] ,于是二分。

    现在已经有了人数,再来差分约束,设第i小时开始工作人数为a[i],每小时至少r[i]个人工作,有t[i]个人愿意在i小时开始工作,那么就有

    \(0\le a[i]\le t[i]\)

    \(a[i]+a[i-1]+\cdots +a[i-7]\ge r[i] (i\ge 9)\)

    \(a[i+17]+a[i+18]+\cdots +a[23]+a[1]+a[2]+\cdots +a[i]\ge r[i] (1\le i \le 8)\)

    但是这样还是没法差分约束,变量太多了。但是变量下标都是连续的\(\to\)前缀和!

    \(s[i]=\sum\limits_{j=1}^{i} a[j]\) ,带入上面的式子:

    \(s[i]-s[i-1]\ge 0\)

    \(s[i]-s[i-1]\le t[i]\)

    \(s[i]-s[i-8]\ge r[i] (i\ge 9)\)

    \(s[i]+s[23]-s[i+16]\ge r[i](1\le i \le 8)\)

    转换成最长路看看有没有正环就好了。

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    int r[30],n,t,T,ans,head[30],num_edge,num[30],cnt[30],dist[30];
    bool inq[30];
    struct edge{
    	int to,nex,val;
    }e[100];
    void add(int from,int to,int val)
    {
    	++num_edge;
    	e[num_edge].nex=head[from];
    	e[num_edge].to=to;
    	e[num_edge].val=val;
    	head[from]=num_edge;
    }
    bool spfa(int x)
    {
    	queue<int>q;
    	for(int i=0;i<=24;++i)
    	{
    		inq[i]=0;
    		cnt[i]=0;
    		dist[i]=-999999999;
    	}
    	q.push(0);
    	inq[0]=true;
    	dist[0]=0;
    	cnt[0]=1;
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		inq[u]=false;
    		for(int i=head[u];i;i=e[i].nex)
    		{
    			int v=e[i].to,w=e[i].val;
    			if(dist[v]<dist[u]+w)
    			{
    				dist[v]=dist[u]+w;
    				if(!inq[v])
    				{
    					q.push(v);
    					inq[v]=true;
    				}
    				++cnt[v];
    				if(cnt[v]>=25)return false;
    			}
    		}
    	}
    	return dist[24]==x;
    }
    bool check(int x)
    {
    	num_edge=0;
    	memset(head,0,sizeof(head));
    	add(0,24,x);
    	for(int i=1;i<=24;++i)add(i-1,i,0);
    	for(int i=1;i<=24;++i)add(i,i-1,-num[i]);
    	for(int i=9;i<=24;++i)add(i-8,i,r[i]);
    	for(int i=1;i<=8;++i) add(i+16,i,r[i]-x);
    	if(spfa(x))return true;
    	else return false;
    }
    int main()
    {
    	scanf("%d",&T);
    	while(T--)
    	{
    		ans=999999999;
    		memset(num,0,sizeof(num));
    		for(int i=1;i<=24;++i)scanf("%d",&r[i]);
    		scanf("%d",&n);
    		for(int i=1;i<=n;++i)scanf("%d",&t),++num[t+1];
    		int ll=0,rr=n;
    		while(ll<=rr)
    		{
    			int mid=(ll+rr)>>1;
    			if(check(mid))ans=mid,rr=mid-1;
    			else ll=mid+1;
    		}
    		if(ans!=999999999)printf("%d\n",ans);
    		else puts("No Solution");
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    《Java技术》第七次作业
    《Java技术》第六次作业
    《Java技术》第五次作业
    《Java技术》第四次作业
    《Java技术》第三次作业
    《Java技术》第二次作业
    《Java技术》第一次作业
    股票——布林带
    股票——指数移动平均线
    股票——简单移动平均线
  • 原文地址:https://www.cnblogs.com/zzctommy/p/12321663.html
Copyright © 2020-2023  润新知