http://acm.hdu.edu.cn/showproblem.php?pid=6598
题意:
给n个人,每个人有2种职业可以选择,给m个关系,对于每一个关系,如果人x和人y属于第一种职业,power增加A,如果人x和人y属于第二种职业,power增加C,如果人x和人y不属于同一种职业,power增加B=A/3+C/4,求每个人选择职业后的最大power。
思路:
最小割割开后,每个顶点确切的属于s或t
s点连接顶点,代表连接的顶点是一种职业,t点连接顶点,代表连接的顶点是另外一种职业。
考虑一个(u,v,A,B,C)
连接方式总贡献为W=A+B+C。总贡献-最小割,使得答案最大。
枚举每一种切割方法,建立方程。
切割a,b,相当于总贡献减掉的(A+B),那么就令a+b=A+B
切割c,d,相当于总贡献减掉的(B+C),令c+d=B+C
切割a,e,d,相当于总贡献减掉(A+C),令a+e+d=A+C
切割b,e,c,相当于总贡献减掉(A+C),令b+e+c=A+C
可得一组解:e=(A+C)/2-B,a=b=(A+B)/2,c=d=(B+C)/2。
(题目给的 b=a/4+c/3 保证e>0,保证没有负环)
这样就使得W-(a+b)=C W-(c+d)=A W-(a+e+d)=B W-(b+e+c)=B。
因为是最小割,就保证了减去的最小,那么减去的最小,答案就最大。
考虑到多个 (u,v,A,B,C) 按照上述建图
对于同一个顶点(可能连s和t各有多条边),因为是最小割,割了连s的一边的某条边,那么连t的所有边都不会割,就保证了每个点确切的属于s或者t, 这样保证了合理性,就能拓展到多个 (u,v,A,B,C)。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define int long long
#define FI first
#define SE second
#define MP make_pair
#define PII pair<int,int>
const int MXLOG = 30;
const LL mod = 998244353;
const int MX = 1e6+5;
const int max_n = 505;
struct no{int to,cap,rev;}; //arc
vector<no>g[max_n]; //图
int level[max_n]; //到起点的距离
int iter[max_n]; //当前弧,在其之前的边已经没用了
void addarc(int s,int e,int c){
g[s].push_back((no){e,c,g[e].size()});
g[e].push_back((no){s,0,g[s].size()-1});
}
//更新层次,即level
void bfs(int s){
memset(level,-1,sizeof(level));
level[s]=0;
queue<int>q;
q.push(s);
while(!q.empty()){
int now=q.front();q.pop();
for(int i=0;i<(int)g[now].size();i++){
no &arc=g[now][i];
if(level[arc.to]!=-1||arc.cap<=0) continue;
level[arc.to]=level[now]+1;
q.push(arc.to);
}
}
}
//寻找增广路
int dfs(int v,int t,int f){
if(v==t) return f;
for(iter[v];iter[v]<(int)g[v].size();iter[v]++){
no &arc=g[v][iter[v]];
if(arc.cap<=0||level[arc.to]!=level[v]+1) continue;
int d=dfs(arc.to,t,min(f,arc.cap));
if(d>0) {
arc.cap=arc.cap-d;
g[arc.to][arc.rev].cap+=d;
return d;
}
}
return 0;
}
int Dinic(int s,int t){
int re=0;
while(1){
bfs(s);
memset(iter,0,sizeof(iter));
if(level[t]==-1) return re;
int f;
while((f=dfs(s,t,LLONG_MAX))>0)
re=re+f;
}
return re;
}
signed main(){
int n,m;
while(cin>>n>>m){
for(int i=0;i<=n;i++)g[i].clear();
LL ans=0;
for(int i=1;i<=m;i++){
int uu,vv,aa,bb,cc;scanf("%I64d%I64d%I64d%I64d%I64d",&uu,&vv,&aa,&bb,&cc);
ans=ans+(LL)aa+(LL)bb+(LL)cc;
addarc(0,uu,aa+bb);
addarc(0,vv,aa+bb);
addarc(uu,n+1,bb+cc);
addarc(vv,n+1,bb+cc);
addarc(uu,vv,aa+cc-bb*2);
addarc(vv,uu,aa+cc-bb*2);
}
ans=ans*2-Dinic(0,n+1);
cout<<ans/2<<'\n';
}
return 0;
}