线性规划对偶入门 & 题解:AT_abc224_h [ABC224H] Security Camera 2
Solution
众所周知 CTT 之前不能考单纯性算法,但没说不能考线性规划的对偶。
容易发现每个数最多为
写成线性规划的形式:
给定变量
显然要进行对偶。不是有
如果有
通过对偶原则,把最大化和最小化颠倒顺序,得到:
这时候把
以及
并且最大化
这个东西就是费用流模板了。
这是一个最大费用最大流,不好。但是注意到总的最大流是确定的(贪心保证),所以可以用一个极大的数
如果你在费用流里面套一个原始对偶,可以做到
这是代码:
#include<bits/stdc++.h>
#define ffor(i,a,b) for(int i=(a);i<=(b);i++)
#define roff(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int MAXV=200+10,MAXE=100000+10;
int lim,ll,rr,m,s,t,mc,mf,tmp,tot=1,ev,deg[MAXV],hd[MAXV],cur[MAXV],dis[MAXV],vis[MAXV];
struct Edge {int to,nxt,f,c;}edge[MAXE];
void add_edge(int u,int v,int f,int c) {return edge[++tot]={v,hd[u],f,c},hd[u]=tot,edge[++tot]={u,hd[v],0,-c},hd[v]=tot,void();}
int spfa(int s,int t) {
ffor(i,s,t) dis[i]=0x3f3f3f3f,vis[i]=0,cur[i]=hd[i];
queue<int> q;
dis[s]=0,q.push(s);
while(!q.empty()) {
int u=q.front();
vis[u]=0,q.pop();
for(int i=hd[u];i;i=edge[i].nxt) {
int to=edge[i].to,f=edge[i].f,c=edge[i].c;
if(!f||dis[to]<=dis[u]+c) continue ;
dis[to]=dis[u]+c;
if(!vis[to]) vis[to]=1,q.push(to);
}
}
return dis[t]!=0x3f3f3f3f;
}
int dinic(int u,int mx,int s,int t) {
if(u==t) return mx;
int res=0;
vis[u]=1;
for(int i=cur[u];i;i=edge[i].nxt) {
int to=edge[i].to,f=edge[i].f,c=edge[i].c; cur[u]=i;
if(dis[to]!=dis[u]+c||vis[to]||!f) continue ;
int tmp=dinic(to,min(mx,f),s,t);
if(tmp) {
res+=tmp,mx-=tmp,mc+=c*tmp,edge[i].f-=tmp,edge[i^1].f+=tmp;
if(!mx) break;
}
else dis[to]=0x3f3f3f3f;
}
return vis[u]=0,res;
}
int main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>ll>>rr;
s=0,t=ll+rr+1;
ffor(i,1,ll) cin>>lim,add_edge(s,i,lim,0);
ffor(i,1,rr) cin>>lim,add_edge(i+ll,t,lim,0);
ffor(i,1,ll) ffor(j,1,rr) cin>>lim,add_edge(i,j+ll,10000000,100-lim);
while(spfa(s,t)) while(lim=dinic(s,10000000,s,t)) mf+=lim;
cout<<100*mf-mc;
return 0;
}