HDU4971 A simple brute force problem.(强连通分量缩点 + 最大权闭合子图)

题目

Source

http://acm.hdu.edu.cn/showproblem.php?pid=4971

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

分析

题目大概说有n个可以获益的项目,还有m个有一定代价的技术问题。解决某个项目需要先解决某些技术问题;而解决某些技术问题又需要解决另外一些技术问题;如果两个技术问题互相依赖,则要同时解决它们。问能获得的最少收益是多少。

m个技术问题看成点,依赖关系看成边,然后求强连通分量并缩点形成DAG,这样就是最大权闭合子图问题了,最小割解决即可。

代码

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define INF (1<<30)
#define MAXN 111
#define MAXM 2222

struct Edge{
    int v,cap,flow,next;
}edge[MAXM];
int vs,vt,NE,NV;
int head[MAXN];

void addEdge(int u,int v,int cap){
    edge[NE].v=v; edge[NE].cap=cap; edge[NE].flow=0;
    edge[NE].next=head[u]; head[u]=NE++;
    edge[NE].v=u; edge[NE].cap=0; edge[NE].flow=0;
    edge[NE].next=head[v]; head[v]=NE++;
}

int level[MAXN];
int gap[MAXN];
void bfs(){
    memset(level,-1,sizeof(level));
    memset(gap,0,sizeof(gap));
    level[vt]=0;
    gap[level[vt]]++;
    queue<int> que;
    que.push(vt);
    while(!que.empty()){
        int u=que.front(); que.pop();
        for(int i=head[u]; i!=-1; i=edge[i].next){
            int v=edge[i].v;
            if(level[v]!=-1) continue;
            level[v]=level[u]+1;
            gap[level[v]]++;
            que.push(v);
        }
    }
}

int pre[MAXN];
int cur[MAXN];
int ISAP(){
    bfs();
    memset(pre,-1,sizeof(pre));
    memcpy(cur,head,sizeof(head));
    int u=pre[vs]=vs,flow=0,aug=INF;
    gap[0]=NV;
    while(level[vs]<NV){
        bool flag=false;
        for(int &i=cur[u]; i!=-1; i=edge[i].next){
            int v=edge[i].v;
            if(edge[i].cap!=edge[i].flow && level[u]==level[v]+1){
                flag=true;
                pre[v]=u;
                u=v;
                //aug=(aug==-1?edge[i].cap:min(aug,edge[i].cap));
                aug=min(aug,edge[i].cap-edge[i].flow);
                if(v==vt){
                    flow+=aug;
                    for(u=pre[v]; v!=vs; v=u,u=pre[u]){
                        edge[cur[u]].flow+=aug;
                        edge[cur[u]^1].flow-=aug;
                    }
                    //aug=-1;
                    aug=INF;
                }
                break;
            }
        }
        if(flag) continue;
        int minlevel=NV;
        for(int i=head[u]; i!=-1; i=edge[i].next){
            int v=edge[i].v;
            if(edge[i].cap!=edge[i].flow && level[v]<minlevel){
                minlevel=level[v];
                cur[u]=i;
            }
        }
        if(--gap[level[u]]==0) break;
        level[u]=minlevel+1;
        gap[level[u]]++;
        u=pre[u];
    }
    return flow;
}

int n,m;

int profit[22],cost[55];
int need[22][55],G[55][55];

int top,stack[MAXN];
bool instack[MAXN];
int dn,dfn[MAXN],low[MAXN];
int bn,belong[MAXN];
void tarjan(int u){
    dfn[u]=low[u]=++dn;
    stack[++top]=u; instack[u]=1;
    for(int v=1; v<=m; ++v){
        if(u==v || G[u][v]==0) continue;
        if(dfn[v]==0){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(instack[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){
        int v; ++bn;
        do{
            v=stack[top--];
            instack[v]=0;
            belong[v]=bn;
        }while(u!=v);
    }
}

int main(){
	int t;
	scanf("%d",&t);
	for(int cse=1; cse<=t; ++cse){
		scanf("%d%d",&n,&m);
		for(int i=1; i<=n; ++i){
			scanf("%d",profit+i);
		}
		for(int i=1; i<=m; ++i){
			scanf("%d",cost+i);
		}
		int a,b;
		for(int i=1; i<=n; ++i){
			need[i][0]=0;
			scanf("%d",&a);
			while(a--){
				scanf("%d",&b);
				need[i][++need[i][0]]=b+1;
			}
		}
		for(int i=1; i<=m; ++i){
			for(int j=1; j<=m; ++j){
				scanf("%d",&G[i][j]);
			}
		}

		top=0; dn=0; bn=0;
		memset(dfn,0,sizeof(dfn));
		memset(instack,0,sizeof(instack));
		for(int i=1; i<=m; ++i){
			if(dfn[i]==0) tarjan(i);
		}
		vs=0; vt=n+bn+1; NV=vt+1; NE=0;
		memset(head,-1,sizeof(head));
		int tot=0;
		for(int i=1; i<=n; ++i){
			tot+=profit[i];
			addEdge(vs,i,profit[i]);
			for(int j=1; j<=need[i][0]; ++j){
				addEdge(i,belong[need[i][j]]+n,INF);
			}
		}
		for(int i=1; i<=bn; ++i){
			int cnt=0;
			for(int j=1; j<=m; ++j){
				if(belong[j]==i) cnt+=cost[j];
			}
			addEdge(i+n,vt,cnt);
		}
		for(int i=1; i<=m; ++i){
			for(int j=1; j<=m; ++j){
				if(G[i][j]==0 || belong[i]==belong[j]) continue;
				addEdge(belong[i]+n,belong[j]+n,INF);
			}
		}
		printf("Case #%d: %d\n",cse,tot-ISAP());
	}
	return 0;
}
时间: 2024-10-04 17:39:26

HDU4971 A simple brute force problem.(强连通分量缩点 + 最大权闭合子图)的相关文章

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

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.(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.(最小割,最大权闭合图)

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

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.

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. 最大权闭合图

点击打开链接 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. Fi

A simple brute force problem.

hdu4971:http://acm.hdu.edu.cn/showproblem.php?pid=4971 题意:给你n个项目,每完成一个项目会有一定的收益,但是为了完成某个项目,要先学会一些技能,学习每个技能会有一定的花费.并且,在学习某项技能之前,可能需要需要先学习前一种技能,最后问你最后能够获得的最大的收益. 题解:这一题,很容易想到网络流,关键是建图,首先把每个技能拆点,两个点之间的容量就是每个技能的花费.人然后右边的边和会点建立一边,容量无穷大,然后就是每个项目的到技能左边的点连接一