Hiho 119 最大权闭合子图的权值等于所有正权点之和减去最小割
题目
时间限制:10000ms
单点时限:1000ms
内存限制:256MB描述
周末,小Hi和小Ho所在的班级决定举行一些班级建设活动。
根据周内的调查结果,小Hi和小Ho一共列出了M项不同的活动(编号1..M),第i项活动能够产生a[i]的活跃值。
班级一共有N名学生(编号1..N),邀请编号为i的同学来参加班级建设活动需要消耗b[i]的活跃值。
每项活动都需要某些学生在场才能够进行,若其中有任意一个学生没有被邀请,这项活动就没有办法进行。
班级建设的活跃值是活动产生的总活跃值减去邀请学生所花费的活跃值。
小Hi和小Ho需要选择进行哪些活动,来保证班级建设的活跃值尽可能大。比如有3项活动,4名学生:
第1项活动产生5的活跃值,需要编号为1、2的学生才能进行;
第2项活动产生10的活跃值,需要编号为3、4的学生才能进行;
第3项活动产生8的活跃值,需要编号为2、3、4的学生才能进行。
编号为1到4的学生需要消耗的活跃值分别为6、3、5、4。
假设举办活动集合为{1},需要邀请的学生集合为{1,2},则得到的班级活跃值为5-9 = -4。
假设举办活动集合为{2},需要邀请的学生集合为{3,4},则得到的班级活跃值为10-9 = 1。
假设举办活动集合为{2,3},需要邀请的学生集合为{2,3,4},则得到的班级活跃值为18-12 = 6。
假设举办活动集合为{1,2,3},需要邀请的学生集合为{1,2,3,4},则得到的班级活跃值为23-18 = 5。小Hi和小Ho总是希望班级活跃值越大越好,因此在这个例子中,他们会选择举行活动2和活动3。
提示:最大权闭合子图
输入
第1行:两个正整数N,M。1≤N≤200,1≤M≤200
第2行:M个正整数,第i个数表示邀请编号为i的学生需要花费的活跃值b[i],1≤b[i]≤1,000
第3..N+2行:第i行表示编号为i的活动情况。首先是2个整数a,k,a表示该活动产生的活跃值,k表示该活动需要的学生人数。接下来k个整数列举该活动需要的学生编号。1≤a≤1,000,1≤k≤M输出
第1行:1个整数,最大可以产生的班级活跃值
解题思路
先把问题转化为2分图。将N个活动看作A部,将M个学生看作B部。若第i个活动需要第j个学生,就连一条从A[i]到B[j]的有向边,那么问题的本质是求解一个最大权闭合子图。
首先重新进行构图:建立源点s和汇点t,将源点s与所有权值为正的点相连,容量为权值;将所有权值为负的点与汇点t相连,容量为权值的绝对值;权值为0的点不做处理;同时将原来的边容量设置为无穷大。
构图完成后,在图上计算最大流,结果为所有正权点之和减去最大流(依据定理最大权闭合子图的权值等于所有正权点之和减去最小割,且最小割等于最大流)。
解题代码
using System;
using System.Collections.Generic;
namespace hiho
{
class _119
{
private static int TotalSize = 0;
private static int[,] rGraph = null;
private static bool HashPath(int[] path)
{
Queue<int> q = new Queue<int>();
q.Enqueue(0);
bool[] visited = new bool[TotalSize];
while (q.Count > 0)
{
int node = q.Dequeue();
for (int i = 0; i < TotalSize; ++i)
{
if (!visited[i] && rGraph[node, i] > 0)
{
q.Enqueue(i);
visited[i] = true;
path[i] = node;
}
}
}
return visited[TotalSize - 1];
}
public static void Main()
{
int ans = 0;
//construct graph
string[] lineArray = Console.ReadLine().Split(' ');
int N = int.Parse(lineArray[0]), M = int.Parse(lineArray[1]);
if (N == 1)
{
Console.WriteLine(1);
return;
}
TotalSize = N + M + 2;
rGraph = new int[TotalSize, TotalSize];
lineArray = Console.ReadLine().Split(' ');
for (int i = 0; i < lineArray.Length; ++i)
rGraph[i + N + 1, TotalSize - 1] = int.Parse(lineArray[i]);
for (int i = 0; i < N; ++i)
{
lineArray = Console.ReadLine().Split(' ');
rGraph[0, i + 1] = int.Parse(lineArray[0]);
ans += int.Parse(lineArray[0]);
for (int j = 2; j < lineArray.Length; ++j)
rGraph[i + 1, int.Parse(lineArray[j]) + N] = int.MaxValue;
}
//maximum flux principle
int[] path = new int[TotalSize];
int minCut = 0;
while (HashPath(path))
{
int minCap = int.MaxValue;
for (int i = TotalSize - 1; i != 0; i = path[i])
if (rGraph[path[i], i] < minCap)
minCap = rGraph[path[i], i];
for (int i = TotalSize - 1; i != 0; i = path[i])
{
rGraph[path[i], i] -= minCap;
rGraph[i, path[i]] += minCap;
}
minCut += minCap;
}
//output the result
Console.WriteLine(ans - minCut);
}
}
}