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 v have 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 6
Sample Output
12
Source
2019 Multi-University Training Contest 2
题意:有n个士兵,每个士兵可以在两种职业(M和W)中二选一,对于士兵有m种连接关系(u,u,A,B,C),表示如果u和v士兵同为M则他们的贡献为A,如果他们同为W他们的贡献为C,如果他们其中一个为M另一个为W则贡献为B(输入保证B = A/4 + C/3),求为每种士兵确定一种职业,而得到最大贡献,输出最大贡献。
题解:网络流菜鸡的我比赛时完全想不到是最小割,看了神仙一般的题解和网上大佬的博客才能理解。
我们先来看一下题解给出的一张图
图上x,y表示两个士兵,源点连向这两个士兵,两个士兵连向汇点,再对两个士兵建一个双向边,根据最小割的定义,我们知道最小割后源点s不能到达汇点t,也就是对于每个士兵,源点到他的边和他到汇点的边只能二选一,也就是满足W和M两职业二选一。
好的理解了构图原理,我们再来计算一下边权:
当两个士兵同时选M职业时,那么在图中我们就要断掉x->t和y->t两条边(因为是最小割所以中间的边不会断),而我们丢失贡献就是B + C,即:c + d = B + C;
同理如果两个士兵都选W职业时,在图中就要断掉s->x和s->y这两条边,我们丢失的贡献就是A + B,即:a + b = A + B;
当x士兵选W职业,y选M职业时,在图中就要断掉s->a,x->y和y->t三条边(因为是最小割所以中间那条边必须断掉),我们丢失的贡献就是A + C,即:a + e + d = A + C;
同理当x士兵选M职业,y选W职业时,我们丢失的贡献同样是A + C,即:b + e + c = A + C。
将B = A / 4 + C / 3 带入进去,我们可以的到一组解:
a = b = 5 / 8 * A + C / 6
c = d = A / 8 + 2 / 3 * C
e = A / 4 + C / 6
因为我们要的是最大贡献,而我们最小割跑出来的是,最小的贡献丢失值,所以我们我们把所有贡献总和减去最小割就是最后的答案
但是题目只保证4|a, 3|b,所以我们可以将边权都乘以二,最后答案再除以2就行了
//#include<bits/stdc++.h>
//#include<unordered_map>
//#include<unordered_set>
#include<iostream>
#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a, b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f;
const int O = 1e6;
const int mod = 998244353;
const int maxn = 1e5 + 5;
const double PI = acos(-1.0);
const double E = 2.718281828459;
const double eps = 1e-8;
struct Edge
{
int from,to;
LL cap,flow;
Edge(int u,int v,LL c,LL f):from(u),to(v),cap(c),flow(f){}
};
struct Dinic
{
int n,m,s,t;//结点数,边数(包括反向弧),源点编号,汇点编号
vector<Edge>edges;//边表,dges[e]和dges[e^1]互为反向弧
vector<int>G[maxn];//邻接表,G[i][j]表示结点i的第j条边在e数组中的编号
bool vis[maxn]; //BFS的使用
int d[maxn]; //从起点到i的距离
int cur[maxn]; //当前弧下标
void addedge(int from,int to,int cap)
{
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
ULL m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool bfs()
{
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(s);
d[s]=0;
vis[s]=1;
while(!Q.empty())
{
int x=Q.front();Q.pop();
for(int i=0;i<G[x].size();i++)
{
Edge&e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)//只考虑残量网络中的弧
{
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
LL dfs(int x, LL a)//x表示当前结点,a表示目前为止的最小残量
{
if(x==t||a==0)return a;//a等于0时及时退出,此时相当于断路了
LL flow=0,f;
for(int&i=cur[x];i<G[x].size();i++)//从上次考虑的弧开始,注意要使用引用,同时修改cur[x]
{
Edge&e=edges[G[x][i]];//e是一条边
if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(!a)break;//a等于0及时退出,当a!=0,说明当前节点还存在另一个曾广路分支。
}
}
return flow;
}
LL Maxflow(int s,int t)//主过程
{
this->s=s; this->t=t;
LL flow=0;
while(bfs())//不停地用bfs构造分层网络,然后用dfs沿着阻塞流增广
{
memset(cur,0,sizeof(cur));
flow+=dfs(s,INF);
}
return flow;
}
};
int main(){
int n, m;
while(cin>>n>>m){
Dinic a; LL sum = 0;
for(int i=0; i<m; i++) {
int u, v, A, B, C; scanf("%d%d%d%d%d", &u, &v, &A, &B, &C);
a.addedge(0, u, A * 5 / 4 + C / 3);
a.addedge(0, v, A * 5 / 4 + C / 3);
a.addedge(u, n+1, A / 4 + 4 * C / 3);
a.addedge(v, n+1, A / 4 + 4 * C / 3);
a.addedge(u, v, A / 2 + C / 3);
a.addedge(v, u, A / 2 + C / 3);
sum += 2 * (A + B + C);
}
LL ans = (sum - a.Maxflow(0, n + 1)) / 2;
printf("%lld\n", ans);
}
return 0;
}