HOJ 2634 How to earn more //最小割

来源:互联网 发布:苹果mac怎么切换中文 编辑:程序博客网 时间:2024/06/02 11:57

How to earn more

Time limit:1sec.Submitted:185Memory limit:64MAccepted:109Source: ww

Xiao Ming is an expert in computer science and technology, so he can get a lot of projects every month. The projects always bring him a lot of money, now he is thinking how to earn money as more as possible.

Every month he can get m projects, and each project Ai will bring him Xi yuan. Although Xiao Ming is an expert, he still needs to hire some other guys to help him. Of course, the employees are not as good as Xiao Ming, for they are just good at some single aspect. So, they should work together to finish one project. There is a list shows the salary of m employees, who are labeled from 0 to m-1. Xiao Ming only hires employees, in that list, and he knows who will be needed by each project.If one employee is hired, he can join in several projects.

 

Input

 

The first line is an integer c shows the number of cases. For each case, the first line has two numbers m,n(m,n <=100), denoting that there is m projects and n employees on the list.The second line has m integers, which are seperated by a single blank, the ith number Ximeans the project Ai will bring Xiao Ming Xi yuan. Xi is less the 10000. The third line has n integers, which are seperated by a single blank, the ith number Yimeans the employee Bi will cost Xiao Ming Yi yuan. And the next m lines will show which part of the employees will be needed by each project. Line i is a list of the employees, who are needed by project Ai. In each line, first a number Zi shows the number of employees needed by this project. And Zi labels of the emloyees follows, which are still seperated by a sigle blank.

 

Output

 

You should output a single integer shows the maximun money Xiao Ming can earn in a single month. The money he can earn is equall to the money he can totally get minus the money he totally cost. You should not leave any extra blanks at the end of each line.

Sample Input

 

 

13 530 40 4355 17 23 22 113 0 1 23 1 2 32 2 1

 

 

Sample Output

 

 

21

 

 

Hint

 

If Xiao Ming can do less project to earn more money, he will certainly do that.
Statistic     Status   Submit   Solution   Discuss    Print
[ Back ]

 

 

整个建图过程参考了这个大牛,http://hi.baidu.com/nicyun/blog/item/c02909301cd18a9ca8018e69.html

 

 

题目大意:有m个项目要做,有n个工人能做,每个项目要且必须要这些工人中的一个子集来完成,给出每个项目完成后的收益gi和雇佣每个工人的花费ci,以及每个项目必须需要的工人的编号,问能获得的最大收益是多少。

先构图,记起始点为s汇点为t,从s到每个工人wi连一条边容量为ci,从每个项目pi到t连一条边容量为gi,然后若项目pi需要工人wi来完成,则从wi到pi连一条边容量为无穷大。

现在看看此图的最小割有什么含义。(设由最小割划分开与s相连的子图为S,与t相连的子图为T,T中的项目和工人称为被选中的)

1. 若选中的项目pi在T中,因为割中不可能有无限容量的边,则完成此项目所需要的工人也必在T中,这样就保证了割对应的是一个可行的方案。

2. s到每个工人wi的边如果在割中,则此工人是被选中的,如果不在则是未被选中的。

3. 相反,项目pi到t的边如果在割中,则此项目是未被选中的,如果不在则是被选中的。(画个图看看就明白了)

4. 可得出最小割的容量为:被选中的工人花费 + 未被选中的项目收入(1)

又 未被选中的项目收入 = 总的项目收入 - 选中的项目收入(2)

所以可得割的容量为: 总的项目收入 - (被选中的项目收入 - 被选中的工人花费)

可以看出括号中的即为题目要求的ans,设总的项目收入为E,由最小割 == 最大流的定理可得:

ans = E - MaxFlow

 

 

 

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,flow[210][210],dist[210],gap[210];
int find_path(int p, int limit=1<<30-1)
{
    if (p == n - 1) return limit;
    for (int i = 0; i < n; ++i)
    {
        if (dist[p] == dist[i] + 1 && flow[p][i] > 0)
        {
            int t = find_path(i, min(limit, flow[p][i]));
            if (t < 0) return t;
            if (t > 0)
            {
                flow[p][i] -= t;
                flow[i][p] += t;
                return t;
            }
        }
    }
    int label = n;
    for (int i = 0; i < n; ++i)
        if (flow[p][i] > 0) label = min(label, dist[i] + 1);
    if (--gap[dist[p]] == 0 || dist[0] >= n) return -1;
    ++gap[dist[p] = label];
    return 0;
}
int iSAP()
{
    gap[0] = n;
    int maxflow = 0,t = 0;
    while ((t = find_path(0)) >= 0) maxflow += t;
    return maxflow;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(flow,0,sizeof(flow));
        memset(gap,0,sizeof(gap));
        memset(dist,0,sizeof(dist));
        scanf("%d%d",&n,&m);
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            flow[i+m][n+m+1]=x;
            sum+=x;
        }
        for(int i=1;i<=m;i++)
        {
            int x;
            scanf("%d",&x);
            flow[0][i]=x;
        }
        for(int i=1;i<=n;i++)
        {
            int t,x;
            scanf("%d",&t);
            for(int j=1;j<=t;j++)
            {
                scanf("%d",&x);
                x++;
                flow[x][i+m]=1<<30-1;
            }
        }
        n=n+m+2;
        printf("%d/n",sum-iSAP());
    }
    return 0;
}

原创粉丝点击