有M个实验,N个仪器。每完成一个实验会得到一定的收益,每个仪器需要一定的费用。给出每个实验需要用到的仪器的集合。求最大收益。
最大权闭合图。请参考最小割模型在信息学竞赛中的应用
这个oj上可以测试太空飞行计划问题 可是我始终只能的81分,从网上找的代码也没有一个能AC的QAQ 谁过了一定要告诉我!QAQ (白花了100积分买数据。。。。
<pre name="code" class="cpp">//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef double DB;
typedef long long ll;
typedef pair<int, int> PII;
#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const DB eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const int maxn = 10000 + 10;
///init是初始化要在加边之前初始化,然后调用max_flow(顶点数,边数,源点, 汇点)
const int maxv = 10000 + 10;///顶点个数
const int maxe = 1000000 + 10;///边数
struct node{
int v, cap, next;
}edge[maxe];
int head[maxv], cnt;
int n;///n是节点个数,m是边数
int st, ed;///st是源点,ed是汇点
int gap[maxv], h[maxv];
void addedge(int u, int v, ll w){///有向图加边
edge[cnt].v = v; edge[cnt].cap = w; edge[cnt].next = head[u]; head[u] = cnt++;///正向边
edge[cnt].v = u; edge[cnt].cap = 0; edge[cnt].next = head[v]; head[v] = cnt++;///反向边
}
int dfs(int x, int cost){
if(x == ed) return cost; ///当前节点是汇点,直接返回cost
int can = cost, d, minh = n - 1;
for(int i=head[x]; ~i; i=edge[i].next){
int v = edge[i].v, w = edge[i].cap;
if(w > 0){///如果这条边的容量大于0
if(h[v] + 1 == h[x]){///如果这是允许弧
if(can > w) d = w;///如果当前弧的容量小于之前可增广的容量
else d = can;
d = dfs(v, d);///从v开始可增广的容量为d
///更新弧的容量和可增广的容量
edge[i].cap -= d;
edge[i ^ 1].cap += d;
can -= d;
if(h[st] >= n) return cost - can;
if(!can) break;///不能再继续增广
}
if(h[v] < minh) minh = h[v];///更新最小标号
}
}
if(can == cost){///如果没有增广...GAP
gap[h[x]]--;
if(gap[h[x]] == 0) h[st] = n;///存在断层,没有增广路了
h[x] = minh + 1;///重新标记,保证下次再访问的时候有流量
gap[h[x]]++;
}
return cost - can;///在这个点之前可以增广的 - 访问这个点之后可以增广的 = 在这个点增广的容量
}
int max_flow(int N, int source, int sink){///SAP+GAP优化
n = N;//m = M;
st = source; ed = sink;
memset(h, 0, sizeof(h));///h[i]表示i节点的标号
memset(gap, 0, sizeof(gap));///gap[i]表示标号为i的节点个数
gap[0] = n;///初始有n个节点标号为0
int ret = 0;
while(h[st] < n){
ret += dfs(st, inf);
}
return ret;
}
void init(){
memset(head, -1, sizeof(head)); cnt = 0;
}
bool vis[maxn];
void Dfs(int x){
vis[x] = true;
for(int i=head[x]; ~i; i=edge[i].next){
if(edge[i].cap > 0 && !vis[edge[i].v]) Dfs(edge[i].v);
}
}
int N, M, x, c;
int main(){
cin >> M >> N;
init();
int sum = 0;
for(int i=1; i<=M; i++){
scanf("%d", &x);
sum += x;///所有实验收入之和
addedge(0, i, x);///从源点向每个实验连一条边
for(; ; ){
do c=getchar(); while (c==' '); ungetc(c,stdin);
if (c==10 || c==13) break;
scanf("%d", &x);
addedge(i, M + x, inf);///从每个实验向它用到的仪器连一条容量是inf的边
}
}
for(int i=1; i<=N; i++){
cin >> x;
addedge(i + M, N + M + 1, x);///从每个仪器向汇点连一条边
}
int ans = sum - max_flow(N + M + 2, 0, N + M + 1);
memset(vis, 0, sizeof(vis));
Dfs(0);///求割集,因为割集是满流的。从源点能遍历到的就和源点在一个集合,剩下的和汇点一个集合
int tot = 0;
for(int i=1; i<=M; i++)
if(vis[i]) tot++;
for(int i=1; i<=M && tot; i++)
if(vis[i]) printf("%d%c", i, --tot == 0 ? '\n' : ' ');
tot = 0;
for(int i=M+1; i<=N+M; i++)
if(vis[i]) tot++;
for(int i=M+1; i<=N+M && tot; i++)
if(vis[i]) printf("%d%c", i - M, --tot == 0 ? '\n' : ' ');
cout << ans << endl;
return 0;
}