LG 1791 人员雇佣
题面
题解
最小割
每个人建一个点,用该点与 \(S\) 或 \(T\) 连通表示不取或取,割断边要支付代价。
可以将两人同时选的收益转化为选人的收益和二选一的代价:
用 \(X_i\in\{0,1\}\) 表示不选/选,则 \(X_i\times X_j=\frac{1}{2}(X_i+X_j-[X_i\neq X_j])\)
于是点 \(i\) 与 \(S\) 连边 \(A_i\) ,与 \(T\) 连边 \(\sum_j E_{i,j}\)
点 \(i\) 与点 \(j\) 连边 \(2\times E_{i,j}\) (均为无向边)
求最小割 \(Min\) , \(Ans=\sum_{i,j} E_{i,j}-Min\)
代码
//https://www.luogu.com.cn/problem/P1791
//20211023 AliCCC
#include <cstdio>
#include <queue>
#include <algorithm>
using std::min;
using std::queue;
const long long LONF=4567891012345678910LL;
const int MAXN=1011;
const int MAXV=MAXN<<1;
const int MAXE=(MAXN<<1)+(MAXN*MAXN); //undirected
int N;
int A[MAXN];
int Ee[MAXN][MAXN];
struct Vert{
int FE;
int Lev, Bfn;
} V[MAXV];
int Vcnt;
int Sink, Sour;
int P[MAXN];
struct Edge{
int y, next;
long long f;
} E[MAXE<<1];
int Ecnt=2;
void addE(const int &a, const int &b, const long long &f){
E[Ecnt].y=b;E[Ecnt].next=V[a].FE;V[a].FE=Ecnt;E[Ecnt].f=f;++Ecnt;
E[Ecnt].y=a;E[Ecnt].next=V[b].FE;V[b].FE=Ecnt;E[Ecnt].f=f;++Ecnt;
}
int BFN;
queue<int> Q;
bool BFS(int at=Sour){
++BFN;
V[at].Lev=1;
Q.push(at);
V[at].Bfn=BFN;
while(!Q.empty()){
at=Q.front();Q.pop();
for(int k=V[at].FE, to;k;k=E[k].next){
if(E[k].f<=0) continue;
to=E[k].y;
if(V[to].Bfn==BFN) continue;
V[to].Lev=V[at].Lev+1;
Q.push(to);
V[to].Bfn=BFN;
}
}
return V[Sink].Bfn==BFN;
}
long long DFS(int at=Sour, long long inc=LONF){
if(at==Sink || inc<=0LL) return inc;
long long ret=0LL, out;
for(int k=V[at].FE, to;k;k=E[k].next){
if(E[k].f<=0) continue;
to=E[k].y;
if(V[to].Lev!=V[at].Lev+1) continue;
out=DFS(to, min(E[k].f, inc));
inc-=out;E[k].f-=out;
ret+=out;E[k^1].f+=out;
}
if(inc>0LL) V[at].Lev=-1;
return ret;
}
long long DINIC(){
long long ret=0LL;
while(BFS()){
ret+=DFS();
// for(int i=0;i<Ecnt;++i){
// printf("%d %d %lld\n", E[i].y, E[i].next, E[i].f);
// }
// printf("--%lld\n", ret);
// puts("");
}
return ret;
}
int main(){
scanf("%d", &N);
Sour=++Vcnt;Sink=++Vcnt;
for(int i=1;i<=N;++i) P[i]=++Vcnt;
for(int i=1;i<=N;++i) scanf("%d", &A[i]);
for(int i=1;i<=N;++i){
addE(Sour, P[i], A[i]);
}
long long Ans=0LL;
for(int i=1;i<=N;++i){
long long tmp=0LL;
for(int j=1;j<=N;++j){
scanf("%d", &Ee[i][j]);
if(Ee[i][j]) addE(P[i], P[j], Ee[i][j]);
tmp+=Ee[i][j];Ans+=Ee[i][j];
}
addE(Sink, P[i], tmp);
}
Ans-=DINIC();
printf("%lld\n", Ans);
return 0;
}
后记
其实 \(0\leq X_i\leq 1\) , \(\max \sum_{i,j} X_iX_jE_{i,j}-\sum_{i,j} [X_i>X_j]E_{i,j}-\sum_{i}X_iA_i\) 是比较经典的线性规划式子,但 Pickupwin 发现自己不会线性规划转网络流了 TAT
求大佬指点 [可怜]