差分约束
差分约束的意味很浓了
差分约束就是 \(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;
}
差分约束的经典,如果很快能推出所有的约束条件,%%%
发现这题如果知道用几个人就很好做了,因为约束条件总是不够,加上人数就可以差分约束了。而且人数还是有上下界的:[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;
}