Duopoly UVALive - 3487
网络流·最小割
题目大意:
有两家公司都想向政府申请某些资源的使用权,并且他们都提供了一些申请列表,列表中含有申请费用和资源种类,同一家公司的申请列表之间不含有重复的资源。政府只可以完整地接受和拒绝谋一份申请列表,问政府的最大收益是多少。
题解:
对于一组,新建一个点连源点(或汇点),流量为费用,再连向组内的全部点(流量为INF),最后跑一遍最小割即可。
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
const int N = 5e5+5;
const int M = 1e6+5;
const int INF = 0x3f3f3f3f;
int n1,n2,S,T;
struct Edge{
int to,next,flow,cap;
}e[M*2];
int head[N], ec=1;
void clear(){ memset(head,0,sizeof(head)); ec=1; }
void add(int a,int b,int c){
ec++; e[ec].to=b; e[ec].cap=c; e[ec].flow=0;
e[ec].next=head[a]; head[a]=ec;
}
void add2(int a,int b,int c){
// D(a); D(b); D(c); E;
add(a,b,c); add(b,a,0);
}
bool vis[N]; int d[N];
bool bfs(){
memset(vis,false,sizeof(vis));
queue<int> q; q.push(S); d[S]=0; vis[S]=true;
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(e[i].cap>e[i].flow && !vis[v]){
vis[v]=true; d[v]=d[u]+1;
if(v==T) return true;
q.push(v);
}
}
}
return false;
}
int dfs(int u,int a){
if(u==T || a==0) return a;
int flow=0,f;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(d[v]==d[u]+1 && (f=dfs(v,min(a,e[i].cap-e[i].flow)))){
flow+=f; a-=f;
e[i].flow+=f; e[i^1].flow-=f;
if(a==0) break;
}
}
if(a) d[u]=-1;
return flow;
}
int mxf(){
int flow=0;
while(bfs()){
flow+=dfs(S,INF);
}
return flow;
}
int main(){
freopen("a.in","r",stdin);
int tt; scanf("%d",&tt); getchar();
for(int cas=0;cas<tt;cas++){
if(cas) puts("");
int x,cost; char cc; int sum=0;
clear(); S=5e5; T=S+1;
scanf("%d",&n1); getchar();
for(int i=1;i<=n1;i++){
scanf("%d",&cost); getchar();
add2(S,i,cost); sum+=cost;
while(scanf("%d%c",&x,&cc)){
add2(i,x+1e5,INF);
if(cc=='\n') break;
}
}
scanf("%d",&n2); getchar();
for(int i=n1+1;i<=n1+n2;i++){
scanf("%d",&cost); getchar();
add2(i,T,cost); sum+=cost;
while(scanf("%d%c",&x,&cc)){
add2(x+1e5,i,INF);
if(cc=='\n') break;
}
}
int ans=mxf();
ans=sum-ans;
printf("Case %d:\n%d\n",cas+1,ans);
}
}