hdu 4971 A simple brute force problem.【最小权闭包】

A simple brute force problem.

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 729    Accepted Submission(s): 381

Problem Description

There's a company with several projects to be done. Finish a project will get you profits. However, there are some technical problems for some specific projects. To solve the problem, the manager will train his employee which may cost his budget. There may be dependencies between technical problems, for example, A requires B means you need to solve problem B before solving problem A. If A requires B and B requires A, it means that you should solve them at the same time. You can select which problems to be solved and how to solve them freely before finish your projects. Can you tell me the maximum profit?

Input

The first line of the input is a single integer T(<=100) which is the number of test cases. 

Each test case contains a line with two integer n(<=20) and m(<=50) which is the number of project to select to complete and the number of technical problem.

Then a line with n integers. The i-th integer(<=1000) means the profit of complete the i-th project.

Then a line with m integers. The i-th integer(<=1000) means the cost of training to solve the i-th technical problem.

Then n lines. Each line contains some integers. The first integer k is the number of technical problems, followed by k integers implying the technical problems need to solve for the i-th project.

After that, there are m lines with each line contains m integers. If the i-th row of the j-th column is 1, it means that you need to solve the i-th problem before solve the j-th problem. Otherwise the i-th row of the j-th column is 0.

Output

For each test case, please output a line which is "Case #X: Y ", X means the number of the test case and Y means the the maximum profit.

Sample Input

4

2 3

10 10

6 6 6

2 0 1

2 1 2

0 1 0

1 0 0

0 0 0

2 3

10 10

8 10 6

1 0

1 2

0 1 0

1 0 0

0 0 0

2 3

10 10

8 10 6

1 0

1 2

0 1 0

0 0 0

0 0 0

2 3

10 10

8 10 6

1 0

1 2

0 0 0

1 0 0

0 0 0

Sample Output

Case #1: 2

Case #2: 4

Case #3: 4

Case #4: 6

Author

BJTU

Source

2014 Multi-University Training Contest 10


题目大意: 

一共有N个工程,有M个问题,想要建立一个工程需要解决一些问题之后才能建立,并且问题之间有依赖关系,建立一个工程会有对应的利润,解决一个问题会有对应的花费,问怎样建立工程才能使得利润最大。


思路;


1、经典的最小权闭包问题,通过求最大流来求得最小割,从而使得最大利润=总利润-最小割。


2、考虑建图:

①建立源点,连入各个工程,流设定为工程的利润。

②建立汇点,将各个问题连入汇点,流设定为各个问题的花费。

③对应一个工程连入其需要解决的问题上,流设定为INF。

那么我们首先不考虑问题的依赖关系,此时若跑最大流,得到最小割,那么其最大利润=总利润-最小割。


3、若我们现在要考虑一个依赖关系。只要考虑将工程的利润的剩余流跑入另外一个问题中即可,那么对应:

④如果有(i,j)=1.对应将问题i连入问题j,流设定为INF。


4、整个图建好之后,跑得最大流,得到最小割,那么其最大利润=总利润-最小割。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<queue>
#include<iostream>
using namespace std;
struct node
{
    int from;
    int to;
    int w;
    int next;
}e[1000*1000*2+50];
int cur[10050];
int divv[10050];
int head[10050];
int val[10050];
int cost[10050];
int n,m,cont,ss,tt,sum;
void add(int from,int to,int w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void Getmap()
{
    memset(head,-1,sizeof(head));
    cont=0;
    ss=n+m+1;
    tt=ss+1;
    for(int i=0;i<n;i++)
    {
        add(ss,i,val[i]);
        add(i,ss,0);
    }
    for(int i=0;i<m;i++)
    {
        add(i+n,tt,cost[i]);
        add(tt,i,0);
    }
    for(int i=0;i<n;i++)
    {
        int k;
        scanf("%d",&k);
        while(k--)
        {
            int x;
            scanf("%d",&x);
            add(i,x+n,0x3f3f3f3f);
            add(x+n,i,0);
        }
    }
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<m;j++)
        {
            int x;
            scanf("%d",&x);
            if(x==1)
            {
                add(i+n,j+n,0x3f3f3f3f);
                add(j+n,i+n,0);
            }
        }
    }
}
int makedivv()
{
    queue<int >s;
    s.push(ss);
    memset(divv,0,sizeof(divv));
    divv[ss]=1;
    while(!s.empty())
    {
        int u=s.front();
        if(u==tt)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(w&&divv[v]==0)
            {
                divv[v]=divv[u]+1;
                s.push(v);
            }
        }
    }
    return 0;
}
int Dfs(int u,int maxflow,int tt)
{
    if(u==tt)return maxflow;
    int ret=0;
    for(int &i=cur[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        int w=e[i].w;
        if(w&&divv[v]==divv[u]+1)
        {
            int f=Dfs(v,min(maxflow-ret,w),tt);
            e[i].w-=f;
            e[i^1].w+=f;
            ret+=f;
            if(ret==maxflow)return ret;
        }
    }
    return ret;
}
void Slove()
{
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        ans+=Dfs(ss,0x3f3f3f3f,tt);
    }
    printf("%d\n",sum-ans);
}
int main()
{
    int t;
    int kase=0;
    scanf("%d",&t);
    while(t--)
    {
        sum=0;
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&val[i]);
            sum+=val[i];
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d",&cost[i]);
        }
        Getmap();
        printf("Case #%d: ",++kase);
        Slove();
    }
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值