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): 157    Accepted Submission(s): 99

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

Source

2014 Multi-University Training Contest 10

有n个工程,完成每个工程能够获得一定利润,有m个技术难关,每个技术难关都要通过培训员工来解决,并且要花费一定金钱。

每个工程都需要克服几个技术难关才能完成,技术难关数量也可以为0.

有的技术难关需要在其他技术难关克服的前提下才能克服。

问获得的最大利润为多少?

闭合图:一个有向图的子点集,使其中的点的出边都指回集合中的点,则称此为闭合图。

最大权闭合图:给每个点赋上点权,则权和最大的闭合图,为最大权闭合图。

闭合图的性质恰好反映了事件之间的必要条件的关系:一个事件发生,它需要的所有前提都要发生。

定义一个割划分出的S集合为一个解,那么割集的容量之和就是(未被选的A集合中的顶点的权值 + 被选的B集合中的顶点的权值),记为Cut。A集合中所有顶点的权值之和记为Total,那么Total - Cut就是(被选的A集合中的顶点的权值
- 被选的B集合中的顶点的权值),即为我们的目标函数,记为A。要想最大化目标函数A,就要尽可能使Cut小,Total是固定值,所以目标函数A取得最大值的时候,Cut最小,即为最小割。

//31MS	756K
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=20010;//点数的最大值
const int MAXM=880010;//边数的最大值
const int INF=0x3f3f3f3f;

struct Node
{
    int from,to,next;
    int cap;
}edge[MAXM];
int tol;
int head[MAXN];
int dis[MAXN];
int gap[MAXN];//gap[x]=y :说明残留网络中dis[i]==x的个数为y

int nn;//nn是总的点的个数,包括源点和汇点

void init()
{
    tol=0;
    memset(head,-1,sizeof(head));
}

void addedge(int u,int v,int w)
{
    edge[tol].from=u;
    edge[tol].to=v;
    edge[tol].cap=w;
    edge[tol].next=head[u];
    head[u]=tol++;
    edge[tol].from=v;
    edge[tol].to=u;
    edge[tol].cap=0;
    edge[tol].next=head[v];
    head[v]=tol++;
}
void BFS(int start,int end)
{
    memset(dis,-1,sizeof(dis));
    memset(gap,0,sizeof(gap));
    gap[0]=1;
    int que[MAXN];
    int front,rear;
    front=rear=0;
    dis[end]=0;
    que[rear++]=end;
    while(front!=rear)
    {
        int u=que[front++];
        if(front==MAXN)front=0;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(dis[v]!=-1)continue;
            que[rear++]=v;
            if(rear==MAXN)rear=0;
            dis[v]=dis[u]+1;
            ++gap[dis[v]];
        }
    }
}
int SAP(int start,int end)
{
    int res=0;nn=end+1;
    BFS(start,end);
    int cur[MAXN];
    int S[MAXN];
    int top=0;
    memcpy(cur,head,sizeof(head));
    int u=start;
    int i;
    while(dis[start]<nn)
    {
        if(u==end)
        {
            int temp=INF;
            int inser;
            for(i=0;i<top;i++)
               if(temp>edge[S[i]].cap)
               {
                   temp=edge[S[i]].cap;
                   inser=i;
               }
            for(i=0;i<top;i++)
            {
                edge[S[i]].cap-=temp;
                edge[S[i]^1].cap+=temp;
            }
            res+=temp;
            top=inser;
            u=edge[S[top]].from;
        }
        if(u!=end&&gap[dis[u]-1]==0)//出现断层,无增广路
          break;
        for(i=cur[u];i!=-1;i=edge[i].next)
           if(edge[i].cap!=0&&dis[u]==dis[edge[i].to]+1)
             break;
        if(i!=-1)
        {
            cur[u]=i;
            S[top++]=i;
            u=edge[i].to;
        }
        else
        {
            int min=nn;
            for(i=head[u];i!=-1;i=edge[i].next)
            {
                if(edge[i].cap==0)continue;
                if(min>dis[edge[i].to])
                {
                    min=dis[edge[i].to];
                    cur[u]=i;
                }
            }
            --gap[dis[u]];
            dis[u]=min+1;
            ++gap[dis[u]];
            if(u!=start)u=edge[S[--top]].from;
        }
    }
    return res;
}
int main()
{
    int tt,cas=1,s,t,n,m;
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        init();
        s=0;
        t=n+m+1;
        int a,num,sum=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a);
            addedge(s,i,a);
            sum+=a;
        }
        for(int i=1; i<=m; i++)
        {
            scanf("%d",&a);
            addedge(n+m+i,t,a);
            addedge(n+i,t,a);
        }
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&num);
            while(num--)
            {
                scanf("%d",&a);
                addedge(i,n+a+1,INF);
            }
        }
        for(int i=1; i<=m; i++)
            for(int j=1; j<=m; j++)
            {
                scanf("%d",&a);
                if(a)addedge(n+i,n+j,INF);
            }
        int ans=SAP(s,t);
        printf("Case #%d: %d\n",cas++,sum-ans);
    }
    return 0;
}
时间: 2024-10-01 07:52:51

HDU 4971 A simple brute force problem. 最大权闭合图的相关文章

HDU 4971 A simple brute force problem.(dp)

HDU 4971 A simple brute force problem. 题目链接 官方题解写的正解是最大闭合权,但是比赛的时候用状态压缩的dp也过掉了- -,还跑得挺快 思路:先利用dfs预处理出每个项目要完成的技术集合,那么dp[i][j]表示第i个项目,已经完成了j集合的技术,由于j这维很大,所以利用map去开数组 代码: #include <cstdio> #include <cstring> #include <algorithm> #include &l

【最小割】HDU 4971 A simple brute force problem.

说是最大权闭合图.... 比赛时没敢写.... 题意 一共有n个任务,m个技术 完成一个任务可盈利一些钱,学习一个技术要花费钱 完成某个任务前需要先学习某几个技术 但是可能在学习一个任务前需要学习另几个任务 求最多能赚多少钱咯 先将缩点将需要一起学掉的技术缩成一个点 建s--任务 权值为该任务盈利多少钱 建技术(缩点后)-t 权值为学习这技术的花费(总) 任务-技术 (完成该任务所需的每个技术都需要建边)权值为INF #include<stdio.h> #include<stdlib.h

HDU - 4971 A simple brute force problem. (DP)

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

HDU 4971 A simple brute force problem. 强连通缩点+最大权闭合图

题意: 给定n个项目,m个技术难题 下面一行n个数字表示每个项目的收益 下面一行m个数字表示攻克每个技术难题的花费 下面n行第i行表示 第一个数字u表示完成 i 项目需要解决几个技术难题,后面u个数字表示需要解决的问题标号. 下面m*m的矩阵 (i,j) = 1 表示要解决j问题必须先解决i问题. (若几个问题成环,则需要一起解决) 问:最大收益. 思路: 先给问题缩点一下,每个缩点后的点权就是这个点内所有点权和. 然后跑一个最大权闭合图. #include<stdio.h> #include

HDU 4971 A simple brute force problem.(最小割,最大权闭合图)

http://acm.hdu.edu.cn/showproblem.php?pid=4971 A simple brute force problem. Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 182    Accepted Submission(s): 115 Problem Description There's a com

hdoj 4971 A simple brute force problem. 【最大闭合权 --&gt; 最小割】

题目:hdoj 4971 A simple brute force problem. 题意:给出 n 个任务和 m 项技术,完成某个任务需要其中几项技术,完成某个任务有奖金,学习某个技术需要钱,技术之间有父子关系,某项技术可能需要先学习其他技术,然后问你选择做那些任务获得收益最大? 分析:看题意的黑体字部分,就是一个标准的闭合权问题,这个题目的关键忽悠点在于技术之间的关系,导致很多人想到了dp以及树形dp. 其实就是一个闭合权问题模板,官方题解说如果技术之间存在相互的关系需要缩点,其实不用缩点也

HDU 4971 A simple brute force problem.

A simple brute force problem. Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on HDU. Original ID: 497164-bit integer IO format: %I64d      Java class name: Main There's a company with several projects to be done. Finish a projec

HDU 4971 A simple brute force problem.(最小割---最大权闭合)

题目地址:HDU 4971 比赛的时候还不会最大权闭合,当时还跟队友讨论了好长时间费用流和DP..现在看来就是一最大权闭合水题... 建图思路:源点连工程,权值为利润,汇点连科技项目,权值为花费,然后对有依赖性的连有向边.用正权值和减去最小割就是答案. #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #in

HDU 4971 A simple brute force problem.(最大权闭合图)

题意:n个项目m个问题,完成每个项目有对应收入,解决每个问题需要对应花费, 给出每个项目需解决的问题以及各问题间的依赖关系,求最大利润(可完成部分或全部项目): 思路:网络流中的最大权闭合图的典型应用——利润问题,参考07年day2国家集训队胡伯涛的论文: 参考:http://blog.csdn.net/u012965890/article/details/38761043 闭合图指所有顶点的出边指向的所有顶点均在图中: 最大权闭合图:点权和最大的闭合图,即权值最大的子闭合图: 建图思路: 工程