最大权闭合子图介绍:
题目链接:
http://61.187.179.71:9988/problem.php?id=1611
题目大意:XX有很多梦想,完成梦想能获得价值X,但是一个梦想需要许多种努力,每种努力可能需要消耗价值也可能得到价值,问在获得最大价值的条件下,最多可完成多少种梦想
解题思路:
最大权闭合子图模板题,梦想和源点连边,可得到价值的努力也和源点连边,会消耗价值的努力和汇点连边,跑一遍网络流得到最小割,用所有正价值的和减去最小割即可。
但是这道题还要求最多能完成多少个梦想,一般情况下可能有多种不同的割边方式能得到我们需求的最大价值,所以我们求得的不一定是最大的。
这里有一个神奇的边权放大法,将所有与源点汇点相连的边乘以一个较大的常数(至少要大于图中梦想的点数),然后再给所有的梦想与源点相连的边的权值加1,这样最后得到的最大价值再除以我们乘的常数就是我们需求的价值,最多的梦想就是源点可以到达的梦想个数,直接在残余网络中dfs一遍统计能到达的梦想点就可以了(能到达的点就是我们建的边和反向边中权值大于0的)。
伪AC代码:
//oj上数据有问题。。(别问我为什么知道,标称都跑不过去,标称跑的结果和我一样。。。)
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <stack>
#include <map>
#include <cstring>
#include <string>
#include <queue>
typedef long long LL;
const LL INF=1e15;
using namespace std;
const LL MAX_V=2100;
struct edge
{
LL to,cap,rev;
edge(LL a,LL b,LL c)
{
to = a;
cap =b;
rev = c;
}
};
vector<edge> g[MAX_V];
LL level[MAX_V];
LL iter[MAX_V];
bool used[MAX_V];
void add_edge(LL from,LL to,LL cap )
{
//cout<<from<<"--¡·"<<to<< " "<<cap<<endl;
g[from].push_back(edge(to,cap,g[to].size()));
g[to].push_back(edge(from,0,g[from].size()-1));
}
void bfs(LL s)
{
memset(level,-1,sizeof(level));
queue<LL> que;
level[s]=0;
que.push(s);
while(!que.empty())
{
LL v=que.front();
que.pop();
for(LL i=0; i<g[v].size(); i++)
{
edge &e=g[v][i];
if(e.cap>0&&level[e.to]<0)
{
level[e.to]=level[v]+1;
que.push(e.to);
}
}
}
}
LL dfs(LL v,LL t,LL f)
{
if(v==t) return f;
for(LL &i=iter[v]; i<g[v].size(); i++)
{
edge &e = g[v][i];
if(e.cap >0&&level[v]<level[e.to])
{
LL d = dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
g[e.to][e.rev].cap +=d;
return d;
}
}
}
return 0;
}
LL max_flow(LL s,LL t)
{
LL flow =0;
for(;;)
{
bfs(s);
if(level[t]<0) return flow;
memset(iter,0,sizeof(iter));
LL f;
while((f=dfs(s,t,INF))>0) flow+=f;
}
}
bool vis[MAX_V];
void dfs1(LL v)
{
vis[v]=true;
for(LL i=0; i<g[v].size(); i++)
{
if(g[v][i].cap>0&&(!vis[g[v][i].to])) {
//cout<<v<<"-->"<<g[v][i].to<<endl;
// cout<<"cap = "<<g[v][i].cap<<endl;
dfs1(g[v][i].to);
}
}
}
int main()
{
//cout<<INF<<endl;
LL m,n;
while(scanf("%I64d %I64d",&n,&m)!=EOF)
{
for(LL i=0; i<MAX_V; i++)
g[i].clear();
LL total=0;
for(LL i=1; i<=n; i++)
{
LL dream;
scanf("%I64d",&dream);
add_edge(0,i,dream*10000+1);
total+=dream*10000+1;
}
for(LL i=1; i<=m; i++)
{
LL tr;
scanf("%I64d",&tr);
if(tr>=0)
{
add_edge(0,n+i,tr*10000);
total+=tr*10000;
}
else add_edge(n+i,n+m+1,(-tr)*10000);
}
LL k;
for(LL i=1; i<=n; i++)
{
scanf("%I64d",&k);
for(LL j=1; j<=k; j++)
{
LL t;
scanf("%I64d",&t);
add_edge(i,n+t,INF);
}
}
LL ans2=0;
LL ans =(total- max_flow(0,n+m+1))/10000;
//cout<<"qq"<<endl;
memset(vis,0,sizeof(vis));
dfs1(0);
for(LL i=1;i<=n;i++)
if(vis[i]) ans2++;
printf("%I64d %I64d\n",ans,ans2);
}
return 0;
}