题意
给出一些实验和器材,每种实验需要一些器材,每种实验如果做了可以获取一定的收益但是要支付实验器材相应的代价。器材如果买过了就不用再买了。现在给出一些实验和器材的收益和价值,求出我们最多能获得多少收益和做了的实验和用了的器材。
思路
收益:先存下实验的一共得收益。我们对一个实验有两种选择:做这个实验,支付器材相应的价值;不做这个实验,减去这个实验可以获得的收益。这样子就变成了求一个图的最小割问题,把实验和源点连一条边,容量为实验的收益,把器材和汇点连一条边,容量为器材代价,实验和器材之间要连一条容量无穷大的边,所以我们不会割这条边。
求做了的实验和器材:在dinic算法中我们会构建分层图,判断实验和器材的深度我们就可以知道有没有做过哪些实验和器材了。
代码
#include<cstdio>
#include<queue>
using namespace std;
int tot,m,n,ans,head[1030],d[1030],f[1030],num,val,S,T,ff;
char c;
struct node{
int to,flow,next;
}e[26010];
void add(int x,int y,int flow)
{
e[++tot].to=y;e[tot].flow=flow;e[tot].next=head[x];
head[x]=tot;
e[++tot].to=x;e[tot].flow=0;e[tot].next=head[y];
head[y]=tot;
}
bool bfs()
{
queue<int> q;
int x,y;
for (int i=1;i<=T;i++) d[i]=0;
q.push(S);d[S]=1;
while (q.size())
{
x=q.front();q.pop();
for (int i=head[x];i;i=e[i].next)
{
y=e[i].to;
if (e[i].flow&&!d[y])
{
d[y]=d[x]+1;
q.push(y);
if (y==T) return 1;
}
}
}
return 0;
}
int dfs(int k,int flow)
{
if (k==T) return flow;
int rest=0,w,y;//rest一定要清0
for (int i=head[k];i;i=e[i].next)
{
y=e[i].to;
if (e[i].flow&&d[y]==d[k]+1)
{
w=dfs(y,min(flow-rest,e[i].flow));
if (!w) d[y]=0;
e[i].flow-=w;
e[i^1].flow+=w;
rest+=w;
}
}
return rest;
}
int main()
{
scanf("%d%d",&m,&n);
tot=1;S=0;T=m+n+1;
for (int i=1;i<=m;i++)
{
scanf("%d",&f[i]);
ans+=f[i];
add(S,i,f[i]);
scanf("%c",&c);
num=0;
while ((scanf("%c",&c)),c!='\n')
{
if (c>='0'&&c<='9') num=num*10+c-48;
else add(i,num+m,2147483647),num=0;
}
add(i,num+m,2147483647);
}
for (int i=1;i<=n;i++)
{
scanf("%d",&val);
add(i+m,T,val);
}
while (bfs())
while (ff=dfs(S,2147483647)) ans-=ff;//最大收益
for (int i=1;i<=m;i++)
if (d[i]) printf("%d ",i);//求做了的实验
printf("\n");
for (int i=m+1;i<=n+m;i++)
if (d[i]) printf("%d ",i-m);//求用过了的器材
printf("\n");
printf("%d",ans);
}