传送门:LA 3487 Duopoly
题目大意:T公司和M公司想向政府申请一些资源的使用权。每一项申请包含一个资源列表和该公司愿意支付的金额。如果该申请得到批准,该公司将得到列表中所有资源的使用权。政府只能完整的批准或拒绝一个申请,不能只批准申请中的部分资源,也不能将一个资源的使用权同时批给两个公司。同一个公司的两项申请中保证不包含相同的资源。你的任务是帮助政府绝对应当批准哪些申请,使得政府收益最大化,即被批准的那些申请的金额之和最大。
题目分析:二分图最小割,建立超级源汇,对所有T公司的申请建边(s,i,cost[ i ]),每项申请与列表中的物品建边(i,x,oo),对所有M公司的申请建边(j,t,cost[ j ]),每项申请与列表中的物品建边(x,j,oo),最后跑一次最大流,所有申请价值之和 - 最大流flow就是最终答案。
其实就是二分图的最大独立集的权值和。
代码如下:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define clear(A, X) memset(A, X, sizeof A)
#define copy(A, B) memcpy(A, B, sizeof A)
using namespace std;
const int maxE = 3000000;
const int maxN = 100000;
const int maxQ = 3000000;
const int oo = 0x3f3f3f3f;
struct Edge{
int v, n, c;
}edge[maxE];
int adj[maxN], cntE;
int Q[maxQ], head, tail;
int d[maxN], cur[maxN], pre[maxN], num[maxN];
int s, t, nv, n, m;
int a[maxN];
char str[maxN];
void addedge(int u, int v, int c){
edge[cntE].v = v; edge[cntE].c = c; edge[cntE].n = adj[u]; adj[u] = cntE++;
edge[cntE].v = u; edge[cntE].c = 0; edge[cntE].n = adj[v]; adj[v] = cntE++;
}
void rev_bfs(){
clear(num, 0);
clear(d, -1);
d[t] = 0;
num[0] = 1;
head = tail = 0;
Q[tail++] = t;
while(head != tail){
int u = Q[head++];
for(int i = adj[u]; ~i; i = edge[i].n){
int v = edge[i].v;
if(~d[v]) continue;
d[v] = d[u] + 1;
Q[tail++] = v;
num[d[v]]++;
}
}
}
int ISAP(){
copy(cur, adj);
rev_bfs();
int flow = 0, u = pre[s] = s, i;
while(d[s] < nv){
if(u == t){
int f = oo, neck;
for(i = s; i != t; i = edge[cur[i]].v){
if(f > edge[cur[i]].c){
f = edge[cur[i]].c;
neck = i;
}
}
for(i = s; i != t; i = edge[cur[i]].v){
edge[cur[i]].c -= f;
edge[cur[i] ^ 1].c += f;
}
flow += f;
u = neck;
}
for(i = cur[u]; ~i; i = edge[i].n) if(d[edge[i].v] + 1 == d[u] && edge[i].c) break;
if(~i){
cur[u] = i;
pre[edge[i].v] = u;
u = edge[i].v;
}
else{
if(0 == (--num[d[u]])) break;
int mind = nv;
for(i = adj[u]; ~i; i = edge[i].n){
if(edge[i].c && mind > d[edge[i].v]){
cur[u] = i;
mind = d[edge[i].v];
}
}
d[u] = mind + 1;
num[d[u]]++;
u = pre[u];
}
}
return flow;
}
void init(){
clear(adj, -1);
cntE = 0;
s = maxN - 4; t = maxN - 3; nv = maxN - 2;
}
void work(){
int w, x, pos = 30000, sum = 0;
init();
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
scanf("%d", &w);
addedge(s, i + pos, w);
sum += w;
fgets(str, maxN, stdin);
x = 0;
for(int j = 0; str[j]; ++j){
if(str[j] < '0' || str[j] > '9'){
if(x) addedge(i + pos, x, oo);
x = 0;
}
else{
x = x * 10 + str[j] - '0';
}
}
}
scanf("%d", &m);
for(int i = 1; i <= m; ++i){
scanf("%d", &w);
addedge(i + pos + n, t, w);
sum += w;
fgets(str, maxN, stdin);
x = 0;
for(int j = 0; str[j]; ++j){
if(str[j] < '0' || str[j] > '9'){
if(x) addedge(x, i + pos + n, oo);
x = 0;
}
else{
x = x * 10 + str[j] - '0';
}
}
}
printf("%d\n", sum - ISAP());
}
int main(){
int cas, t;
for(scanf("%d", &t), cas = 1; cas <= t; ++cas){
if(cas > 1) printf("\n");
printf("Case %d:\n", cas);
work();
}
return 0;
}