【bzoj1391】[Ceoi2008]order 网络流最小割

原文地址:http://www.cnblogs.com/GXZlegend/p/6796937.html


题目描述

有N个工作,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序需要某种机器来完成,你可以通过购买或租用机器来完成。 现在给出这些参数,求最大利润

输入

第一行给出 N,M(1<=N<=1200,1<=M<=1200) 下面将有N块数据,每块数据第一行给出完成这个任务能赚到的钱(其在[1,5000])及有多少道工序 接下来若干行每行两个数,分别描述完成工序所需要的机器编号及租用它的费用(其在[1,20000]) 最后M行,每行给出购买机器的费用(其在[1,20000])

输出

最大利润

样例输入

2 3
100 2
1 30
2 20
100 2
1 40
3 80
50
80
110

样例输出

50


题解

最小割,类似最大权闭合图

首先考虑如果不能租用机器的话,那么就是一个最大权闭合图的水题,S向工作连容量为利润的边,工作向机器连容量为inf的边,机器向T连容量为费用的边,答案为sum-mincut。

那么加上了租用机器该怎么办呢?其实是类似的。

最大权闭合图求法中边容量为inf,目的是防止割断该边。本题中想割断工作与机器之间的边,则边容量不为inf,而是租用机器的费用。

这与最大权闭合图求法的证明差不多,自己画一下推导推导就行了。

此题需要加弧优化。

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
queue<int> q;
int head[3000] , to[4000000] , val[4000000] , next[4000000] , cnt = 1 , cur[4000000] , s , t , dis[3000];
void add(int x , int y , int z)
{
	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
	to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
}
bool bfs()
{
	int x , i;
	while(!q.empty()) q.pop();
	memset(dis , 0 , sizeof(dis));
	dis[s] = 1 , q.push(s);
	while(!q.empty())
	{
		x = q.front() , q.pop();
		for(i = head[x] ; i ; i = next[i])
			if(val[i] && !dis[to[i]])
				dis[to[i]] = dis[x] + 1 , q.push(to[i]);
	}
	return dis[t] > 0;
}
int dinic(int x , int low)
{
	if(x == t) return low;
	int temp = low , i , k;
	for(i = cur[x] ; i ; i = next[i])
	{
		if(val[i] && dis[to[i]] == dis[x] + 1)
		{
			k = dinic(to[i] , min(temp , val[i]));
			if(!k) dis[to[i]] = 0;
			val[i] -= k;
			if(val[i]) cur[x] = i;
			val[i ^ 1] += k;
			if(!(temp -= k)) break;
		}
	}
	return low - temp;
}
int main()
{
	int n , m , i , a , k , p , c , sum = 0;
	scanf("%d%d" , &n , &m);
	s = 0 , t = n + m + 1;
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%d%d" , &a , &k) , add(s , i , a) , sum += a;
		while(k -- ) scanf("%d%d" , &p , &c) , add(i , p + n , c);
	}
	for(i = 1 ; i <= m ; i ++ ) scanf("%d" , &a) , add(i + n , t , a);
	while(bfs())
	{
		for(i = s ; i <= t ; i ++ ) cur[i] = head[i];
		sum -= dinic(s , 0x7fffffff);
	}
	printf("%d\n" , sum);
	return 0;
}

 

posted @ 2017-05-02 16:54  GXZlegend  阅读(268)  评论(0编辑  收藏  举报