差分约束&&01规划
差分约束
给定不等式组,求可行解
最大值\(\to\) 最短路;最小值\(\to\)最长路
\(x \geq y+s\) 从 y 向 x 引一条边权为s的边
\(x \leq y+s\) 从 y 向 x 引一条边权为s的边
找一个源点,使其可以遍历所有的边
从源点求一遍最长/短路
结果1:如果存在负环,则原不等式组一定无解
结果2:如果没有负环,则\(dis[i]\)就是原不等式组的一个可行解
1.糖果 (求最小值)
- 如果 X=1,表示第 A个小朋友分到的糖果必须和第 B个小朋友分到的糖果一样多;
- 如果 X=2,表示第 A个小朋友分到的糖果必须少于第 B个小朋友分到的糖果;
- 如果 X=3,表示第 A个小朋友分到的糖果必须不少于第 B个小朋友分到的糖果;
- 如果 X=4,表示第 A个小朋友分到的糖果必须多于第 B个小朋友分到的糖果;
- 如果 X=5,表示揶 A个小朋友分到的糖果必须不多于第 B个小朋友分到的糖果。
x=1 : \(A=B\) \(addd(x,y,0),addd(y,x,0);\)
x=2 : \(A \textless B \to B\geq A+1\) \(addd(x,y,1);\)
x=3 : \(A \le B\to B \geq A\) \(addd(y,x,0);\)
x=4 : \(A \textgreater B \to A \geq B+1\) \(addd(y,x,1);\)
x=5 : \(A\geq B\) \(addd(x,y,0);\)
2.区间(AcWing362)
给定 n 个区间 \([ai,bi]\) 和 \(n\) 个整数 \(c_i\) 。你需要构造一个整数集合 Z,使得∀i∈[1,n],Z 中满足\(a_i≤x≤b_i\)的整数 x 不少于$c_i $个。
\(s[i]表示前i个数中选择了几个\)
n个条件转化为 \(s[y]-s[x-1]\geq c_i \to s[y]\geq s[x-1]+c_i\) \(addd(x,y,c_i)\)
\(s[i] \geq s[i-1],s[i]-s[i-1]\leq 1\) \(addd(i-1,i,0),addd(i,i-1,-1)\)
\(spfa(min a)\) 求最小值,最长路
3.出纳员问题(AcWing393 )
枚举答案\([0,n]\)
\(addd(0,24,ans),addd(24,0,-ans);\) 点24为最终的答案;
\(0 \to 24 的最长路若>ans,说明ans个人达不到要求,会形成正环,没有正环,说明答案可行\)
而且24小时是个循环,考虑边的指向
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=30,M=3010;
int hd[N],to[M],nx[M],d[M],id;
int dis[N],a[N],t[N],c[N];
void addd(int x,int y,int z){to[++id]=y,d[id]=z,nx[id]=hd[x],hd[x]=id;}
queue<int>q;
bitset<N>ck;
bool spfa()
{
int x=0;
memset(dis,-0x3f,sizeof(dis));
memset(c,0,sizeof(c));
dis[x]=0;
q.push(x);
while(!q.empty())
{
x=q.front(),q.pop();
ck[x]=0;
for(int i=hd[x];i;i=nx[i])
{
if(dis[to[i]]<dis[x]+d[i])
{
dis[to[i]]=dis[x]+d[i];
c[to[i]]=c[x]+1;
if(c[to[i]]>=25) return 0;
if(!ck[to[i]])
{
ck[to[i]]=1;
q.push(to[i]);
}
}
}
}
return 1;
}
void sol()
{
int n,x;
memset(t,0,sizeof(t));
per(i,1,24) scanf("%d",a+i);
cin>>n;
per(i,1,n) scanf("%d",&x),t[x+1]++;
per(ans,0,n)
{
id=0;
memset(hd,0,sizeof(hd));
addd(0,24,ans);
addd(24,0,-ans);
per(i,0,23) addd(i,i+1,0);//不能往下减人
per(i,1,24) addd(i,i-1,-t[i]);//每个小时可用
per(i,0,24)//每个小时需要
{
if(i<=16) addd(i,i+8,a[i+8]);
else addd(i,i-16,a[i-16]-ans);
}
if(spfa())//没有环,符合要求
{
printf("%d\n",ans);
return;
}
}
puts("No Solution");
}
signed main()
{
int tt;
cin>>tt;
while(tt--) sol();
return 0;
}
01规划
1.观光奶牛(AcWing361)
求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。
设最大比值为X
$\frac{\sum f[i]}{\sum t[i]}>X \to \sum f[i]>\sum t[i]*x $
\(\sum f[i]-\sum t[i]*x>0\) ,所以变成找正环
2.最优高铁环
最小平均值环
设\(X=(w_1+w_2+...+w_k)/k\)
\(w_1+...+w_k=k*X\)
\((w_1-x)+(w_2-x)+...+(w_k-x)=0\)
X 稍微大一点 =就会变成 < 就会出现负环
二分逼近找 X
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=5e4+10,M=N<<1,inf=0x3f3f3f3f,p=13331;
const double eps=0.01;
int n,hd[N<<1],to[M],nx[M],d[M],id;
double dis[N];
void addd(int x,int y,int z){to[++id]=y,d[id]=z,nx[id]=hd[x],hd[x]=id;}
bitset<N>ck;
string s;
map<unsigned long long ,int>st;
int get(int x) {
switch (x) {
case 'S': return 1000;
case 'G': return 500;
case 'D': return 300;
case 'T': return 200;
default: return 150;
}
}
bool dfs(int x,double mid)
{
ck[x]=1;
for(int i=hd[x];i;i=nx[i])
{
if(dis[to[i]]>dis[x]+mid+d[i])
{
dis[to[i]]=dis[x]+mid+d[i];
if(ck[to[i]]||dfs(to[i],mid)) return 1;
}
}
ck[x]=0;
return 0;
}
bool sol(double x)
{
per(i,1,n) dis[i]=inf;
ck.reset();
per(i,1,n) if(dfs(i,x)) return 1;
return 0;
}
signed main()
{
int m,co;
unsigned long long u,v;
cin>>m;
per(i,1,m)
{
cin>>s;
u=0,v=0,co=0;
for(char &c:s)
{
if(c=='-') u=(u?u:v),v=0;
else co+=(v?0:get(c)),v=v*p+c;
}
if(!st.count(u)) st[u]=++n;
if(!st.count(v)) st[v]=++n;
addd(st[u],st[v],-co);
}
double l=0,r=2147483648,mid,ans=-1;
while(r-l>eps)
{
mid=(l+r)/2;
if(sol(mid)) l=ans=mid;
else r=mid;
}
cout<<round(ans)<<'\n';
return 0;
}