问题描述
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合
E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1, I2,…In}。 实验 Ej需要用到的仪器是I的子集jÍI。配置仪器Ik的费用
为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置
哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。
编程任务
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
数据输入
由文件input.txt提供输入数据。文件第 1行有 2 个正整数 m和 n。m是实验数,n是仪器数。接下来的 m 行,每行是一个实验的有关数据。第一个
数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的 n个数是配置每个仪器的费用。
结果输出
程序运行结束时,将最佳实验方案输出到文件 output.txt 中。第 1 行是实验编号;第 2 行是仪器编号;最后一行是净收益。
输入文件示例
input.txt
2 3
10 1 2
25 2 3
5 6 7
输出文件示例
output.txt
1 2
1 2 3
17
题解
这道题如果不输出方案还是比较简单的一道网络流题目,但是让输出方案就非常蛋疼了。
蒟蒻一开始使用的方法是跑完dinic以后判断每个仪器是否被使用,并推出选择了哪些实验,然而只过了两个点QAQ
于是开始面向数据
开始修改方法,在推出实验以后再次根据实验重新推出选择了哪些仪器,AC数据点++,rp–
发现dinic写残了,AC数据点++,rp–(如果哪位神犇明白蒟蒻的错误请留言指正QwQ)
请教了机房中的各位dalao,发现别人都是在bfs时加入vis数组,每次清空更新,最后根据vis数组输出答案。
原理:每次将vis数组清空,根据最小割和最大流的关系,在我们找到一条remain不为0的边以后,这条边指向的点一定是没有被割掉的,所以在最后一次bfs时,也就是没有bfs到汇点的那一次,当我们更新到一个仪器,并且由实验连到这个仪器的边的remain不为0,就说明这个仪器没有被割掉,反过来由于实验和仪器之间的边的remain为INF,所以又将实验加入到答案中。
CODE:
#include<cstdio>
#include<cstring>
const int INF=1e9;
struct queue
{
int h,t;
int a[10001];
inline void clear(){h=1,t=0;}
inline void push(int n){a[++t]=n;}
inline int front(){return a[h];}
inline void pop(){h++;}
inline bool empty(){return h>t;}
}q;
struct edge
{
int next,to,remain;
}a[10000];
int w[10000];
char s[10000];
int head[1000];
int deep[1000];
bool b[1000];
int n,m,num=1,sum,x,tot,S,T,ans;
inline int min(int a,int b){return a<b?a:b;}
inline int getstring()
{
int p=-1;
char c=getchar();
while(c<'0'||c>'9') c=getchar();
while((c>='0'&&c<='9')||c==' ') s[++p]=c,c=getchar();
s[++p]='$';
return p;
}
inline int getnum(int len)
{
int p=0,id=0,n;
while(p!=len)
{
n=0;
while(s[p]==' ') p++;
while(s[p]>='0'&&s[p]<='9') n=n*10+s[p]-48,p++;
w[++id]=n;
}
return id;
}
inline void add(int x,int y,int cap)
{
a[++num].next=head[x],a[num].to=y,a[num].remain=cap,head[x]=num;
a[++num].next=head[y],a[num].to=x,head[y]=num;
}
inline bool bfs(int s,int t)
{
memset(deep,0x3f,sizeof(deep));
memset(b,0,sizeof(b));
deep[s]=0;
q.clear();q.push(s);
while(!q.empty())
{
int tmp=q.front();q.pop();
for(int i=head[tmp];i;i=a[i].next)
if(deep[a[i].to]>INF&&a[i].remain)
deep[a[i].to]=deep[tmp]+1,q.push(a[i].to),b[a[i].to]=1;
}
return deep[t]<INF;
}
int dfs(int now,int t,int limit)
{
if(now==t||!limit) return limit;
int flow=0,f;
for(int i=head[now];i;i=a[i].next)
if(deep[a[i].to]==deep[now]+1&&a[i].remain&&(f=dfs(a[i].to,t,min(limit,a[i].remain))))
{
flow+=f;
limit-=f;
a[i].remain-=f;
a[i^1].remain+=f;
if(!limit) return flow;
}
deep[now]=-1;
return flow;
}
inline int dinic(int s,int t)
{
int ans=0;
while(bfs(s,t)) ans+=dfs(s,t,INF);
return ans;
}
int main()
{
scanf("%d%d",&m,&n);
//1~m:实验 m+1~m+n:仪器 m+n+1:源点 m+n+2:汇点
S=m+n+1;T=S+1;
for(int i=1;i<=m;i++)
{
scanf("%d",&x);
sum+=x;
add(S,i,x);
tot=getnum(getstring());
for(int j=1;j<=tot;j++)
add(i,m+w[j],INF);
}
for(int i=1;i<=n;i++)
scanf("%d",&x),add(m+i,T,x);
ans=sum-dinic(S,T);
for(int i=1;i<=m;i++)
if(b[i]) printf("%d ",i);
printf("\n");
for(int i=m+1;i<=m+n;i++)
if(b[i]) printf("%d ",i-m);
printf("\n%d",ans);
return 0;
}