题目:太空飞行计划问题
思路:
把所有的实验和s连边,权值为实验的费用;
把所有的材料和t连边,权值为材料的费用;
再把实验和对应的材料连边,权值为inf。
求一遍最小割,输出下路径。
注意一下,最小割的路径和最大流不同。
一条边被割裂的条件,是一端与s相连,另一端不与s相连,即d值一个为0,一个非0。
update(2021/10/28):
求出最小割后,全图被分为了两部分,一部分节点和S相连,代表选,另一部分和T相连,代表不选。
而和S相连的实验被割掉代表这个实验不被选择,和T相连的器材被割掉代表这个器材被选择。
答案=总实验经费 - 不选的实验费用 - 选的器材费用 = 最小割。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 10000
#define inf ((int)1e9)
struct Edge{
int u,v,w;
Edge(){}
Edge(int uu,int vv,int ww) {u=uu,v=vv,w=ww;}
};
int n,m;
int sum=0;
Edge e[maxn+5];
int nxt[maxn+5],g[maxn+5],cnt=-1; //邻接表存边
int cur[maxn+5]; //cur 当前弧优化
int d[maxn+5]; //分层图
void add(int u,int v,int w) {
e[++cnt]=Edge(u,v,w);
nxt[cnt]=g[u];
g[u]=cnt;
}
void readin() { //建图
memset(g,-1,sizeof(g));
memset(nxt,-1,sizeof(nxt));
char c[maxn+5];
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++) {
int v;
scanf("%d",&v);
sum+=v;
add(0,i,v);
add(i,0,0);
memset(c,0,sizeof(c));
cin.getline(c,maxn+5);
int ulen=0,num;
while (sscanf(c+ulen,"%d",&num)==1) {
add(i,n+num,inf);
add(n+num,i,0);
if (num==0) ulen++;
else while (num) {
num/=10;
ulen++;
}
ulen++;
}
}
for(int i=1;i<=m;i++) {
int v;
scanf("%d",&v);
add(i+n,maxn,v);
add(maxn,i+n,0);
}
}
queue<int> que;
bool bfs() { //分层图
memset(d,0,sizeof(d));
d[0]=1;
que.push(0);
while(!que.empty()) {
int h=que.front();que.pop();
for(int i=g[h];~i;i=nxt[i]) {
Edge y=e[i];
if(y.w==0||d[y.v]) continue;
d[y.v]=d[h]+1;
que.push(y.v);
}
}
if(d[maxn]>0) return true;
else return false;
}
int dfs(int x,int w) { //增广
if(x==maxn) return w;
for(int& i=cur[x]; ~i;i=nxt[i]) {
Edge y=e[i];
if(d[y.v]-d[x]!=1||!y.w) continue;
int z=dfs(y.v,min(w,y.w));
if(z) {
e[i].w-=z;
e[i^1].w+=z;
return z;
}
}
return 0;
}
int slv() {
int ans=0;
while(bfs()) {
for(int i=0;i<=m+n;i++) cur[i]=g[i];cur[maxn]=maxn;
while(int x=dfs(0,inf)) ans+=x;
}
return ans;
}
int p[maxn+5],q[maxn+5];
void print() {
for(int i=1;i<=n;i++) if(d[i]) printf("%d ",i);printf("\n");
for(int j=1;j<=m;j++) if(d[j+n]) printf("%d ",j);printf("\n");
}
int main() {
readin();
int ans=slv();
print();
printf("%d\n",sum-ans);
return 0;
}