LA 3487 Duopoly 最小割

传送门: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;
}




评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值