Problem Description
Now, Bob is playing an interesting game in which he is a general of a harmonious army. There are n soldiers in this army. Each soldier should be in one of the two occupations, Mage or Warrior. There are m pairs of soldiers having combination ability. There are three kinds of combination ability. If the two soldiers in a pair are both Warriors, the army power would be increased by a. If the two soldiers in a pair are both Mages, the army power would be increased by c. Otherwise the army power would be increased by b, and b=a/4+c/3, guaranteed that 4|a and 3|c. Your task is to output the maximum power Bob can increase by arranging the soldiers' occupations.
Note that the symbol a|b means that a divides b, e.g. , 3|12 and 8|24.Input
There are multiple test cases.
Each case starts with a line containing two positive integers n(n≤500) and m(m≤104).
In the following m lines, each line contains five positive integers u,v,a,b,c (1≤u,v≤n,u≠v,1≤a,c≤4×106,b=a/4+c/3), denoting soldiers u and vhave combination ability, guaranteed that the pair (u,v) would not appear more than once.
It is guaranteed that the sum of n in all test cases is no larger than 5×103, and the sum of m in all test cases is no larger than 5×104.Output
For each test case, output one line containing the maximum power Bob can increase by arranging the soldiers' occupations.
Sample Input
3 2
1 2 8 3 3
2 3 4 3 6Sample Output
12
题意:有 n 个士兵 m 个组合,每个组合以 x y a b c 的形式给出,代表当士兵 x 和 y 一组时,两个都为战士时战力+a,两个都为法师时战力+c,其他情况的专职战力+b,且 b=a/4+c/3,问最多能加多少战力
思路:
实质是要将 n 个士兵分为两部分,考虑到数据范围并不大,想办法建图求最小割,由于求最大,因此在求出最小割后用总和减去即为答案
首先设置一个超级源点和超级汇点,然后将 n 个士兵抽象为一个点,对于每个点,从源点连一条边,从汇点连一条边,分别表示选择两种职业
那么一条边的贡献可能为 A、B、C,对于相互连接的两个点 x、y,有:
- x、y 都选战士:a+b=A+B
- x、y 都选法师:c+d=B+C
- x 选战士,y 选法师:a+d+c=A+C
- x 选法师,x 选战士:a+c+e=A+C
那么可得一组解:
- a=(A+B)/2
- b=(A+B)/2
- c=(B+C)/2
- d=(B+C)/2
- e=-B+(A+C)/2
在得到解后,将 m 组具有关系的两点的图进行合并建图:
S->x:a、S->y:a、x->T:c、y->T:c、x->y:e、y->x:e
最后求最小割,用总贡献减去最小割的值即为答案
Source Program
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<LL,LL>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL quickModPow(LL a,LL b,LL mod){ LL res=1; a=a%mod; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1;} return res; }
LL getInv(LL a,LL mod){ return quickModPow(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-10;
const int MOD = 1E9+7;
const int N = 10000+5;
const int dx[] = {-1,1,0,0,1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
struct Edge{
LL from,to;
LL cap,flow;
Edge(){}
Edge(LL from,LL to,LL cap,LL flow):from(from),to(to),cap(cap),flow(flow){}
};
LL n,m; //结点数,边数(含反向弧)
LL S,T; //源点、汇点
vector<Edge> edges; //边表,edges[e]和edges[e^1]互为反向弧
vector<LL> G[N]; //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
bool vis[N]; //BFS使用,标记一个节点是否被遍历过
LL dis[N]; //dis[i]表从起点s到i点的距离(层次)
LL cur[N]; //cur[i]表当前正访问i节点的第cur[i]条弧
void addEdge(LL from,LL to,LL cap){
edges.push_back( Edge(from,to,cap,0) );
edges.push_back( Edge(to,from,0,0) );
LL m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS(){//构建层次网络
memset(vis,0,sizeof(vis));
dis[S]=0;
vis[S]=true;
queue<LL> Q;//用来保存节点编号
Q.push(S);
while(!Q.empty()){
LL x=Q.front();
Q.pop();
for(LL y=0;y<G[x].size();y++){
Edge& e=edges[G[x][y]];
if(!vis[e.to] && e.cap>e.flow){
vis[e.to]=true;
dis[e.to]=dis[x]+1;
Q.push(e.to);
}
}
}
return vis[T];
}
LL DFS(LL x,LL cp){//cp表示从s到x目前为止所有弧的最小残量
if(x==T || cp==0)
return cp;
LL flow=0,newFlow;//flow用来记录从x到t的最小残量
for(LL &y=cur[x];y<G[x].size();y++){
Edge &e=edges[G[x][y]];
if(dis[x]+1==dis[e.to]){
LL minn=min(cp,e.cap-e.flow);
newFlow=DFS(e.to,minn);
if(newFlow>0){
e.flow+=newFlow;
edges[G[x][y]^1].flow-=newFlow;
flow+=newFlow;
cp-=newFlow;
if(cp==0)
break;
}
}
}
return flow;
}
LL Dinic(){
LL flow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow+=DFS(S,INF);
}
return flow;
}
int main(){
while(scanf("%lld%lld",&n,&m)!=EOF){
for(LL i=0;i<=n+1;i++)
G[i].clear();
edges.clear();
LL sum=0;
S=0,T=n+1;
while(m--){
LL x,y;
LL A,B,C;
scanf("%lld%lld%lld%lld%lld",&x,&y,&A,&B,&C);
A*=2; B*=2; C*=2;
sum+=A+B+C;
LL a=(A+B)/2;
LL c=(B+C)/2;
LL e=-B+(A+C)/2;
addEdge(S,x,a);
addEdge(S,y,a);
addEdge(x,T,c);
addEdge(y,T,c);
addEdge(x,y,e);
addEdge(y,x,e);
}
printf("%lld\n",(sum-Dinic())/2);
}
return 0;
}