Luogu P2762
题意
给出m个实验,给出每个实验需要哪些仪器的编号和完成该实验的收益。给出每个仪器的花费。每个仪器都可以在多次使用。求最大收益。并输出具体方案(完成了哪些实验,用了哪些仪器)
题解
不能贪心地解决,可能A实验配置仪器后是亏的,但是配置完仪器后其他的实验要配的仪器变少了,总的收益是更大的。
那么解决这一类A事件的发生和B事件是否发生存在依赖关系,且每个事件都有一定收益(正收益或负收益)的问题,可以用网络流中一个叫做最大权闭合图的模型来解决。推荐一篇论文算法合集之《最小割模型在信息学竞赛中的应用》,胡伯涛大佬写的,其中有对该模型的详细讲解,写的非常好。这篇论文也加深了我对网络流问题的理解。
那么这类问题怎么解决,具体的证明过程看论文,这里只给出建图方法。
原来事件之间的依赖关系对应建一条流量为inf的边,超级源点向每个正收益的事件连一条流量为该事件收益的边,每个亏损的事件向超级汇点连一条流量为亏损的边。跑出的最大流即最小割是总体的最小亏损,拿所有正收益的事件求和,再减去这个最小亏损即是最大收益。
那么还有一个问题,怎么求出方案。这里简单讲一下方法和大致原理。跑完最大流后搜一下残量网络,能与起点联通的事件都选上。原理大致是最小割割掉的那些事件全部舍弃,剩下的就都是需要的事件。
对应到这道题目,做实验是一个正收益的事件,但它依赖于配置仪器这些负收益的事件,那么这样就可以套用上述模型解决了。
代码
直接粘的上古代码......
#include<bits/stdc++.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX 233
using namespace std;
int n,m,ANS=0,T;
int Du[MAX];
vector <int> f[MAX],flow[MAX],FB[MAX];
int dis[MAX];
int dl[2333];
bool BFS()
{
int t=0,w=1,x,X;
memset(dis,0xff,sizeof(dis));
dl[1]=0;dis[0]=0;
do
{
x=dl[++t];
for(int i=0;i<Du[x];i++)
{
X=f[x][i];
if(dis[X]>=0||!flow[x][i]) continue;
dis[X]=dis[x]+1;
dl[++w]=X;
}
}while(t!=w);
if(dis[T]>0) return true;
return false;
}
int DFS(int x,int MFLOW)
{
if(x==T) return MFLOW;
int X,h;
for(int i=0;i<Du[x];i++)
{
X=f[x][i];
if(dis[X]==dis[x]+1&&flow[x][i]>0&&(h=DFS(X,MIN(flow[x][i],MFLOW))))
{
flow[x][i]-=h;
flow[X][FB[x][i]]+=h;
return h;
}
}
return 0;
}
void readin();
void work();
void print();
int main()
{
readin();
work();
print();
return 0;
}
void readin()
{
int x;
char c;
scanf("%d%d",&n,&m);
T=n+m+1;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);getchar();ANS+=x;
Du[0]++;Du[i]++;
f[0].push_back(i);flow[0].push_back(x);FB[0].push_back(Du[i]-1);
f[i].push_back(0);flow[i].push_back(0);FB[i].push_back(Du[0]-1);
while(1)
{
scanf("%d",&x);x+=n;
c=getchar();
Du[i]++;Du[x]++;
f[i].push_back(x);flow[i].push_back(0x7fffffff);FB[i].push_back(Du[x]-1);
f[x].push_back(i);flow[x].push_back(0);FB[x].push_back(Du[i]-1);
if(c==13||c==10) break;
}
}
for(int i=1;i<=m;i++)
{
scanf("%d",&x);
Du[i+n]++;Du[T]++;
f[i+n].push_back(T);flow[i+n].push_back(x);FB[i+n].push_back(Du[T]-1);
f[T].push_back(i+n);flow[T].push_back(0);FB[T].push_back(Du[i+n]-1);
}
}
void work()
{
int x;
while(BFS())
while(x=DFS(0,0x7fffffff))
ANS-=x;
BFS();
}
void print()
{
for(int i=1;i<=n;i++)
if(dis[i]>=0)
printf("%d ",i);
putchar('\n');
for(int i=1;i<=m;i++)
if(dis[i+n]>=0)
printf("%d ",i);
putchar('\n');
printf("%d",ANS);
}