题型:网络流
题意:
有n个项目,m个技术问题。做项目会挣钱,解决技术问题需要花钱。想要完成一个项目,就要解决对应的技术问题,求能获得的最多的利润。
分析:
赤果果的最大权闭合图问题。
先学习一下闭合图的概念。
闭合图就是,对于一个点集V,如果他们的所有的边所指向的点都在点集V中,则这个点集加上他们的边是闭合图。
如图:
1、2、4、5组成的图就是闭合图,3、4、5组成的图也是闭合图,而1、2、5组成的图就不是闭合图,因为点1有一条边连出去了。
每个点有权值正负零都有可能,选某个点u,如果必须选v,则我们设u,v有一条有向边。最后要我们有一种选取方案使得所选的点权值和最大。这就是最大权闭合图。
解法用最大流来解。
建图方法是:s源点到所有正权点连流量为那个点权值的边,所有负权点到汇点t连流量为负权绝对值的边。原来图中的依赖关系正常建边,流量定为inf。
跑出来的最大流就是最小割。用所有正权的和减去最小割就是可获得的最大权。
证明:
因为中间的都是inf,所以最小割肯定是割到与s连的或者与t连的边。那么就是我们选择一些正权或者负权,并且我们都是选小的。也就是说对于正权到s这里,如果正权很小,并且选他就必须选一些负权很大的边,那么我们在最小割中也会选这个正权小的,使得st不连通。反之,如果负权那里小,我们就会选择割负权。最后正权的总和减去最小割。
对于本题,可以清楚的看出可以转化为最大权闭合图来求解:
首先由于技术问题的图可能形成环,所以需要先缩点,建成新的图,下面构造网络流的图。
源点S与每个项目建边,权值为项目的收入;
项目与所对应的技术问题建边,权值为inf;
技术问题与技术问题之间,有前驱关系的技术问题建边,例如i是j的前驱,则j->i,权值为inf;
每一个技术问题与汇点T建边,权值为技术问题的花费。
最大利润等于完成所有项目的收入减去最大流。
代码:
一不小心突破本人单个文件代码长度上限,9000+B不解释~
#include<iostream>
#include<cstdio>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;
const int M = 123456;
const int inf = 0x7f7f7f7f;
int profit[30],cost[60];
class Tarjan { ///Tarjan 算法有向图强连通分量缩点
public:
struct E {
int u,v,next;
} e[M<<4]; ///Bcnt 强连通分量的个数,num 个分量的点数,belong属于哪个分量
int le,head[M],Index,Bcnt,num[M],belong[M],dfn[M],low[M];
bool instack[M];
stack<int> s;
void tarjan(int u) {
dfn[u]=low[u]=++Index;
instack[u]=true;
s.push(u);
int v;
for(int i=head[u]; ~i; i=e[i].next) {
v=e[i].v;
if(!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
} else if(instack[v]) {
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]) {
Bcnt++;
do {
v=s.top();
s.pop();
instack[v]=false;
belong[v]=Bcnt;
num[Bcnt]++;
} while(u!=v);
}
}
void init() {
le=Index=Bcnt=0;
mt(head,-1);
mt(num,0);
mt(dfn,0);
mt(low,0);
mt(instack,0);
while(!s.empty()) s.pop();
}
void add(int u,int v) {
e[le].u=u;
e[le].v=v;
e[le].next=head[u];
head[u]=le++;
}
void solve(int n) {
for(int i=1; i<=n; i++) {
if(!dfn[i]) {
tarjan(i);
}
}
}
int getbcnt() {
return Bcnt;
}
int getbelong(int id) {
return belong[id];
}
int getnum(int id) {
return num[id];
}
} gx;
class Dinic { //最大流
struct E {
int u,v,next,flow;
} e[M<<1];
int le,flow,head[M],temp[M],cur[M],level[M],path[M];
bool used[M];
queue<int> q;
public:
int getflow() {
return flow;
}
bool bfs(int s,int t) {
mt(level,-1);
while(!q.empty()) q.pop();
q.push(s);
level[s]=1;
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=head[u]; ~i; i=e[i].next) {
int v=e[i].v;
if(level[v]==-1&&e[i].flow) {
level[v]=level[u]+1;
q.push(v);
if(v==t) return true;
}
}
}
return false;
}
void init() {
le=0;
mt(head,-1);
}
void add(int u,int v,int flow) {
e[le].u=u;
e[le].v=v;
e[le].flow=flow;
e[le].next=head[u];
head[u]=le++;
e[le].u=v;
e[le].v=u;
e[le].flow=0;
e[le].next=head[v];
head[v]=le++;
}
void solve(int s,int t) {
int p,now,tempp;
bool flag;
flow=0;
while(bfs(s,t)) {
for(int i=0; i<M; i++) {
temp[i]=head[i];
used[i]=true;
}
p=1;
path[p]=s;
while(p) {
int u=path[p];
if(u==t) {
now=inf;
for(int i=1; i<p; i++) {
now=min(now,e[cur[path[i]]].flow);
}
flow+=now;
for(int i=1; i<p; i++) {
e[cur[path[i]]].flow-=now;
e[cur[path[i]]^1].flow+=now;
if(!e[cur[path[i]]].flow) tempp=i;
}
p=tempp;
} else {
flag=false;
for(int i=temp[u]; ~i; i=e[i].next) {
int v=e[i].v;
if(used[v]&&e[i].flow&&level[u]+1==level[v]) {
cur[u]=i;
temp[u]=e[i].next;
flag=true;
path[++p]=v;
break;
}
}
if(flag) continue;
p--;
used[u]=false;
}
}
}
}
} ts;
struct project_problems {
int num;
int question[55];
} project[25];
int Map[55][55];
struct Node {
int value;
} newgraph[55];
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int _,cas = 0;
int n,m;
int allprofit;
scanf("%d",&_);
while(_--) {
scanf("%d%d",&n,&m);
allprofit = 0;
for(int i=0; i<n; i++) {
scanf("%d",&profit[i]);
allprofit += profit[i];
}
for(int i=0; i<m; i++) {
scanf("%d",&cost[i]);
}
///网络流初始化
ts.init();
///缩点初始化
gx.init();
///输入项目的问题
for(int i=0; i<n; i++) {
int num;
scanf("%d",&num);
project[i].num = num;
for(int j=0; j<num; j++) {
scanf("%d",&project[i].question[j]);
}
}
///输入problems之间的关系
int tmp;
for(int i=0; i<m; i++) {
for(int j=0; j<m; j++) {
scanf("%d",&tmp);
Map[i][j] = tmp;
if(tmp == 1) {
gx.add(i+1,j+1);
}
}
}
///缩点
gx.solve(m);
// printf("jihenum======%d\n",gx.getbcnt());
///建成新的图
for(int i=0; i<=m; i++) {
newgraph[i].value = 0;
}
for(int i=0; i<m; i++) {
newgraph[gx.getbelong(i+1)].value += cost[i];
}
// puts("******************");
// for(int i=1;i<=gx.getbcnt();i++){
// printf("%d ",newgraph[i].value);
// }
// puts("");
// puts("******************");
///网络流建图
///源点S为n+m+1
///汇点T为n+m+2
int s = n+m+1;
int t = n+m+2;
///S与project连接
for(int i=0; i<n; i++) {
ts.add(s,i,profit[i]);
}
///project与problem建图
for(int i=0; i<n; i++) {
for(int j=0; j<project[i].num; j++) {
project[i].question[j] = gx.getbelong(project[i].question[j]+1);
}
sort(project[i].question,project[i].question+project[i].num);
project[i].num = unique(project[i].question,project[i].question+project[i].num) - project[i].question;
for(int j=0; j<project[i].num; j++) {
ts.add(i,project[i].question[j]+n,inf);
}
}
// puts("**************************");
// for(int i=0;i<n;i++){
// for(int j=0;j<project[i].num;j++){
// printf("%d ",project[i].question[j]);
// }
// puts("");
// }
// puts("**************************");
bool flag[55][55];
mt(flag,false);
///problem与problem建图
for(int i=0; i<m; i++) {
for(int j=0; j<m; j++) {
if(gx.getbelong(i+1)!=gx.getbelong(j+1) && Map[i][j]==1 && !flag[gx.getbelong(i+1)][gx.getbelong(i+1)]) {
ts.add(gx.getbelong(i+1)+n,gx.getbelong(j+1)+n,inf);
flag[gx.getbelong(i+1)][gx.getbelong(j+1)] = true;
}
}
}
///problem与T连接
for(int i=1; i<=gx.getbcnt(); i++) {
// printf("i=%d t cost =%d\n",i,newgraph[i].value);
ts.add(i+n,t,newgraph[i].value);
}
///跑最大流
ts.solve(s,t);
// printf("flow = %d\n",ts.getflow());
printf("Case #%d: %d\n",++cas,allprofit - ts.getflow());
}
return 0;
}
/**
123
3 5
10 10 10
1 2 3 4 5
2 2 3
3 1 2 3
5 0 1 2 3 4
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 1 0 0 1
0 0 0 0 0
12
2 3
10 10
8 10 6
1 0
1 2
0 1 0
0 0 0
0 0 0
100
10 20
60 65 10 48 31 14 14 4 92 30
5 7 6 43 24 10 30 40 5 3 28 31 43 25 42 16 43 45 27 30
2 1 11
6 11 6 2 3 17 15
15 6 4 13 16 7 8 10 2 3 1 11 17 0 18 5
2 4 8
17 11 14 18 7 0 10 12 17 8 9 2 16 3 15 1 6 4
8 10 12 16 6 2 19 3 13
7 3 17 18 12 15 2 16
3 0 3 8
2 4 8
2 9 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
*/