最大权闭合图
昨天在做多校联赛的时候偶然碰上的,学习了下这个,首先推荐胡伯涛的《最小割模型在信息学竞赛中的应用》。
定义
一个有向图 的闭合图(closure)
G=(V,E)
是该有向图的一个点集,且该点集的所有出边都还指向该点集。即闭合图内的任意点的任意后继也一定在闭合图中。更形式化地说,闭合图是这样的一个点集
V′∈V
,满足对于
∀u∈V
引出的
∀⟨u,v⟩∈E
。那么给每个点
v
分配一个点权
那么,我们的做法就是当
Wv
是负的时候,建一条边
⟨v,t⟩
容量为
−Wv
,当
Wv
是正的时候,建一条边
⟨s,v⟩
容量为
Wv
,然后对于原来的边
⟨u′,v′⟩
保持不变,建一条
⟨u′,v′⟩
容量为
inf
的边。那么易证这个图的在最小割的情况下,和
s
相联通的子图是最大权闭合子图。
所以我们做法就是跑一次最大流,然后最大权闭合图的权值就是总收益-最大流。
POJ2987
这是一个非常裸地最大权闭合图,当
Wv
是负的时候,建一条边
⟨v,t⟩
容量为
−Wv
,当
Wv
是正的时候,建一条边
⟨s,v⟩
容量为
Wv
,然后对于原来的边
⟨u′,v′⟩
保持不变,建一条
⟨u′,v′⟩
容量为
inf
的边。
然后直接按照原理跑一下最大流就好。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
#define N 5010
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
#define pb(x) push_back((x))
typedef long long ll;
typedef unsigned long long ull;
struct edge{
int to,c,rev;
};
vector<edge> g[N];
ll sum;
int level[N],iter[N];
int n,m;
void addnode(int a,int b,int c){
g[a].push_back((edge){b,c,g[b].size()});
g[b].push_back((edge){a,0,g[a].size()-1});
}
void build_graph(int s,int t,int n,int m){
int tmp,u,v;
for(int i=1;i<=n;i++){
scanf("%d",&tmp);
if(tmp>=0){
addnode(s,i,tmp);
sum+=tmp;
}
else addnode(i,t,-tmp);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
addnode(u,v,inf);
}
}
bool bfs(int s,int t){
memset(level,-1,sizeof(iter));
queue <int> q;
level[s]=0;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<g[u].size();i++){
edge e=g[u][i];
if(e.c>0&&level[e.to]==-1){
level[e.to]=level[u]+1;
q.push(e.to);
}
}
}
if(level[t]==-1)return false;
return true;
}
int dfs(int u,int t,int f){
if(u==t) return f;
for(int &i=iter[u];i<g[u].size();i++){
edge &e=g[u][i];
if(e.c>0&&level[e.to]>level[u]){
int d=dfs(e.to,t,min(e.c,f));
if(d>0){
e.c-=d;
g[e.to][e.rev].c+=d;
return d;
}
}
}
return 0;
}
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
sum=0;
build_graph(0,n+1,n,m);
ll ans=0;
while(bfs(0,n+1)){
memset(iter,0,sizeof(iter));
int f;
while((f=dfs(0,n+1,inf))>0){
ans+=f;
}
}
bfs(0,n+1);
int fire=0;
for(int i=1;i<=n;i++) if(level[i]>0) fire++;
printf("%d %lld\n",fire,sum-ans);
for(int i=0;i<=n+1;i++) g[i].clear();
}
return 0;
}
HDU4971
水题。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
#define N 110
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
#define pb(x) push_back((x))
typedef long long ll;
typedef unsigned long long ull;
struct edge{
int to,c,rev;
};
vector<edge> g[N];
ll sum;
int level[N],iter[N];
int n,m;
void addnode(int a,int b,int c){
g[a].push_back((edge){b,c,g[b].size()});
g[b].push_back((edge){a,0,g[a].size()});
}
void build_graph(int s,int t,int n,int m){
int tmp,u,v,k;
for(int i=1;i<=n;i++){
scanf("%d",&tmp);
addnode(s,i,tmp);
sum+=tmp;
}
for(int i=1;i<=m;i++){
scanf("%d",&tmp);
addnode(i+n,t,tmp);
}
for(int i=1;i<=n;i++){
scanf("%d",&k);
for(int j=1;j<=k;j++){
scanf("%d",&tmp);
addnode(i,n+tmp+1,inf);
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++){
scanf("%d",&tmp);
if(tmp) addnode(n+i,n+j,inf);
}
}
}
bool bfs(int s,int t){
memset(level,-1,sizeof(iter));
queue <int> q;
level[s]=0;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<g[u].size();i++){
edge e=g[u][i];
if(e.c>0&&level[e.to]==-1){
level[e.to]=level[u]+1;
q.push(e.to);
}
}
}
if(level[t]==-1)return false;
return true;
}
int dfs(int u,int t,int f){
if(u==t) return f;
for(int &i=iter[u];i<g[u].size();i++){
edge &e=g[u][i];
if(e.c>0&&level[e.to]>level[u]){
int d=dfs(e.to,t,min(e.c,f));
if(d>0){
e.c-=d;
g[e.to][e.rev].c+=d;
return d;
}
}
}
return 0;
}
int main(){
int n,m,t,cnt=0;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
sum=0;
build_graph(0,n+m+1,n,m);
ll ans=0;
while(bfs(0,n+m+1)){
memset(iter,0,sizeof(iter));
int f;
while((f=dfs(0,n+1+m,inf))>0){
ans+=f;
}
}
printf("Case #%d: %d\n",++cnt,sum-ans);
for(int i=0;i<=n+m+1;i++) g[i].clear();
}
return 0;
}
HDU5855
首先膜拜一下朝鲜队的出题大哥,题目质量太好了。
这道题我们直接二分最短的时间,然后对于每一次时间判断一下最大权闭合图是不是大于L就好
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
#define N 410
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
#define pb(x) push_back((x))
typedef long long ll;
typedef unsigned long long ull;
struct edge{
int to,c,rev;
};
vector<int> shop[N];
vector<edge> g[N];
int level[N];
int iter[N];
int n,m,L,tmp1,tmp2;
int pt[N],pay[N],pro[N];
int sum;
inline void addnode(int a,int b,int c){
g[a].push_back((edge){b,c,g[b].size()});
g[b].push_back((edge){a,0,g[a].size()-1});
}
inline void build_graph(int mid){//建图
for(int i=1;i<=m;i++){
bool f=true;
addnode(0,i,pro[i]);
for(int j=0;j<shop[i].size();j++){
addnode(i,m+shop[i][j],inf);
}
}
for(int i=1;i<=n;i++){
if(pt[i]>mid) addnode(m+i,n+m+1,inf);
else addnode(m+i,n+m+1,pay[i]);
}
}
inline bool bfs(int s,int t){
memset(level,-1,sizeof(level));
queue <int> q;
level[s]=0;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<g[u].size();i++){
edge &e=g[u][i];
if(e.c>0&&level[e.to]<0){
level[e.to]=level[u]+1;
q.push(e.to);
}
}
}
if(level[t]==-1) return false;
return true;
}
inline int dfs(int u,int t,int f){
if(u==t) return f;
for(int &i=iter[u];i<g[u].size();i++){
edge &e=g[u][i];
if(e.c>0&&level[e.to]>level[u]){
int d=dfs(e.to,t,min(f,e.c));
if(d>0){
e.c-=d;
g[e.to][e.rev].c+=d;
return d;
}
}
}
return 0;
}
inline int dinic(int mid){
int ans=0;
build_graph(mid);
while(bfs(0,n+m+1)){
memset(iter,0,sizeof(iter));
int f;
while((f=dfs(0,n+m+1,inf))>0){
ans+=f;
}//循环增广
}
for(int i=0;i<=n+m+1;i++) g[i].clear();
if(ans==0) return 0;
//printf("ans=%d %d\n",ans,sum-ans);
return sum-ans;
}
int main(){
int t;
int maxpt;
int cnt=0;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&L);
int maxpt=0;
sum=0;
for(int i=1;i<=n;i++){
scanf("%d%d",&pay[i],&pt[i]);
maxpt=max(pt[i],maxpt);
}
for(int i=1;i<=m;i++){
scanf("%d",&pro[i]);
sum+=pro[i];
scanf("%d",&tmp1);
for(int j=1;j<=tmp1;j++){
scanf("%d",&tmp2);
shop[i].push_back(tmp2);
}
}
int r=maxpt+1,l=0;
tmp1=0;
while(l<r){
//printf("%d %d ",l,r);
int mid=(l+r)/2;
if(l+1==r){
tmp1=dinic(r);
//printf("tmp1= %d\n",tmp1);
if(tmp1>=L){
int ptmp1=dinic(l);
//printf("tmp1= %d\n",ptmp1);
if(ptmp1>=L) tmp1=ptmp1;
else l=r;
}
else l=r+1;
break;
}
tmp1=dinic(mid);
if(tmp1>=L) r=mid;
else l=mid;
//printf("end= %d\n",tmp1);
}
printf("Case #%d: ",++cnt);
if(l<=maxpt){
printf("%d %d\n",l,tmp1);
}
else printf("impossible\n");
for(int i=1;i<=m;i++) shop[i].clear();
}
return 0;
}