W教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集。配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。
Input
多组数据输入.
每组输入第1行有2 个正整数m和n。m是实验数,n是仪器数。接下来的m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接下来是该实验需要的仪器数;接着是对应仪器的编号。最后一行的n个数是配置每个仪器的费用。
Output
每组输出最佳实验方案的净收益
Sample Input
2 3
10 2 1 2
25 2 2 3
5 6 7
Sample Output
17
题目出自nefu476
题意中文就不描述了。
思路:
最大权闭合图,转化为求最小割,最终为最大流。
最大利润 = 总收入 — 最大权闭合图权值 即
最大利润 = 总收入 — 最大流
明确了考察对象之后,注意到关键的就是图的建立;
首先要增加一个源点,一个汇点,然后源点向实验各引一条边,权值为实验经费,各器材向汇点引一条边,权值为支出,中间的实验与对应器材之间连一条边,权值为maxlongint ,图就建立成功。
接下来就直接用最大流算法求出最大流,用总收入减去最大流即可。
为什么这样可以求出割呢?因为最小割中的边必定满流,它的逆否命题:不满流的边必定不是最小割的边也一定成立,因此不满流的边连接的两个点一定在同一个集之中,证毕。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
using namespace std;
typedef long long ll;
const int oo=1e9;/**oo 表示无穷大*/
const int mm=111111111;/**mm 表示边的最大数量,记住要是原图的两倍,在加边的时候都是双向的*/
const int mn=2010;/**mn 表示点的最大数量*/
int node,src,dest,edge;/**node 表示节点数,src 表示源点,dest 表示汇点,edge 统计边数*/
int ver[mm],flow[mm],nex[mm];
int head[mn],work[mn],dis[mn],q[mn];
void prepare(int _node, int _src,int _dest)
{
node=_node,src=_src,dest=_dest;
for(int i=0; i<=node; ++i)head[i]=-1;
edge=0;
}
void addedge( int u, int v, int c)
{
ver[edge]=v,flow[edge]=c,nex[edge]=head[u],head[u]=edge++;
ver[edge]=u,flow[edge]=0,nex[edge]=head[v],head[v]=edge++;
}
bool Dinic_bfs()
{
int i,u,v,l,r=0;
for(i=0; i<node; ++i)dis[i]=-1;
dis[q[r++]=src]=0;
for(l=0; l<r; ++l)
for(i=head[u=q[l]]; i>=0; i=nex[i])
if(flow[i]&&dis[v=ver[i]]<0)
{
dis[q[r++]=v]=dis[u]+1;
if(v==dest) return 1;
}
return 0;
}
int Dinic_dfs( int u, int exp)
{
if(u==dest) return exp;
for( int &i=work[u],v,tmp; i>=0; i=nex[i])
if(flow[i]&&dis[v=ver[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0)
{
flow[i]-=tmp;
flow[i^1]+=tmp;
return tmp;
}
return 0;
}
int Dinic_flow()
{
int i,ret=0,delta;
while(Dinic_bfs())
{
for(i=0; i<node; ++i)work[i]=head[i];
while((delta=Dinic_dfs(src,oo)))ret+=delta;
}
return ret;
}
int num[1005],nun[1005],a[1005][1005];
int main()
{
int n,m;
while(~scanf("%d%d",&m,&n))
{
int sum=0;
for(int i=1; i<=m; i++)
{
scanf("%d%d",&num[i],&a[i][0]);
sum+=num[i];
for(int j=1; j<=a[i][0]; j++)
scanf("%d",&a[i][j]);
}
for(int i=1; i<=n; i++)
scanf("%d",&nun[i]);
prepare(n+m+2,0,n+m+1);
for(int i=1; i<=m; i++)
{
addedge(0,i,num[i]);
for(int j=1; j<=a[i][0]; j++)
addedge(i,a[i][j]+m,nun[a[i][j]]);
}
for(int i=m+1; i<=m+n; i++)
addedge(i,n+m+1,nun[i-m]);
printf("%d\n",sum-Dinic_flow());
}
return 0;
}