【BZOJ 3438】小M的作物 最大权闭合子图

236 篇文章 0 订阅
17 篇文章 0 订阅

有两种建图方法,这里采用最大权闭合子图

首先这里基于最大权闭合子图的思想,但是题目中有两种田,而网络流中的s和t有事代表正边权和负边权,所以考虑去掉一个田,我们就假设先全部将种子种在A田然后不断地将种子向B田移。

闭合图基本就是这个样子:


红色是种子,点权可正可负,绿色是B田的组合方式,点权为正,棕色是A田的组合方式,点权为负

1.对于每一个种子,代价为b[i]-a[i]可正可负,按照情况与s或者t连边,边权为|b[i]-a[i]|

2.A的组合与t连边,权值为C[a],所有组成A方式的种子向他连边,边权为inf

3.s与B的组合,权值为C[B],与所有组成的种子两边,边权为inf

#include<cstdio>  
#include<cstring>  
#include<iostream>  
#define maxn 2000021  
#define inf 0x3fffffff  
using namespace std;
int head[maxn],n,m,tot,h[maxn],last[maxn],ans,s,t,q[maxn];  
struct edge{int v,next,w;}e[maxn];  
void adde(int a,int b,int c){  
    e[tot].v=b,e[tot].next=head[a],e[tot].w=c;head[a]=tot++;  
    e[tot].v=a,e[tot].next=head[b],e[tot].w=0;head[b]=tot++;  
}  
bool bfs(){  
    for(int i=s;i<=t;i++)h[i]=-1;  
    int l=0,r=1;q[l]=s;h[s]=0;  
    while(l<r){  
        int u=q[l++];  
        for(int v,i=head[u];i!=-1;i=e[i].next){  
            if(h[v=e[i].v]==-1&&e[i].w){  
                h[v]=h[u]+1;  
                q[r++]=v;  
            }  
        }  
    }  
    return h[t]!=-1;  
}  
int dfs(int u,int f){  
    if(u==t||!f)return f;  
    int used=0,w;  
    for(int v,i=last[u];i!=-1;i=e[i].next){  
        last[u]=i;  
        if(h[v=e[i].v]!=h[u]+1||!e[i].w)continue;  
        w=min(e[i].w,f-used);  
        w=dfs(v,w);  
        e[i].w-=w,e[i^1].w+=w,used+=w;  
        if(used==f)return used;  
    }  
    if(!used)h[u]=-1;  
    return used;  
}  
void dinic(){  
    while(bfs()){  
        for(int i=s;i<=t;i++)last[i]=head[i];  
        ans-=dfs(s,inf);  
    }  
}  
int a[maxn],b[maxn];
void build(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",a+i),ans+=a[i];
	for(int i=1;i<=n;i++)scanf("%d",&b[i]),b[i]-=a[i];
	scanf("%d",&m);t=n+2*m+1;
	for(int i=1;i<=n;i++){
		if(b[i]>0){
			ans+=b[i],adde(s,i,b[i]);
		}else adde(i,t,-b[i]);
	}
	for(int id,k,c1,c2,i=1;i<=m;i++){
		scanf("%d%d%d",&k,&c1,&c2);
		for(int x,j=1;j<=k;j++){
			scanf("%d",&x);
			adde(n+i,x,inf);adde(x,n+m+i,inf);
		}
		ans+=c1,ans+=c2;
		adde(s,n+i,c2),adde(n+m+i,t,c1);
	}
}  
int main(){  
    memset(head,-1,sizeof(head)); 
    build();
	dinic();
    printf("%d",ans);
    return 0;  
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值