图论:最小割

题目来自:点击这里

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
const int maxm=2000100;
const int maxd=2000100;
const int inf=0x7ffffff;
using namespace std;
int n;
int tot=0;
int edge_num=1;
//0 做源点 0-n+1为中间节点 n+1 做汇点 n+2 - n+1+2m为虚拟节点 
//反向建一个无穷的边,则这条边不会被割掉
int cur[maxd];

struct Edge{
    
    
    int to;
    int w;
    int next;
}edge[maxm];
int head[maxd];
int dep[maxd];

inline void addedge(int from,int to,int dis){
    
    
    edge[++edge_num].next=head[from],edge[edge_num].to=to,edge[edge_num].w=dis,head[from]=edge_num;
    edge[++edge_num].next=head[to],edge[edge_num].to=from,edge[edge_num].w=0,head[to]=edge_num;
}

bool bfs(){
    
    //dinic算法用bfs分层 
	memset(dep,-1,sizeof(dep));
	memcpy(cur,head,sizeof(head));
	queue<int>q;
	q.push(0);//0作为源点 
	dep[0]=0;//深度为0 
	int u,v;
	while(!q.empty()){
    
    
		u=q.front();
		q.pop();
		for(int i=head[u];i;i=edge[i].next){
    
    
			v=edge[i].to;
			if(dep[v]==-1&&edge[i].w>0){
    
    
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return (dep[n+1]!=-1);
}
int dfs(int u,int flow){
    
    //dfs求増广路 ,u是当前节点,flow是当前流量 
	if(u==n+1) return flow;//到达汇点,返回 
	int flowsum=0,flowmin;
	for(int i=cur[u];i;i=edge[i].next){
    
    //遍历与u 相连的所有节点 
		cur[u]=i;
		int v=edge[i].to;
		if(dep[v]==dep[u]+1&&edge[i].w>0){
    
    //满足层次且流量大于0 
			flowmin=dfs(v,min(flow,edge[i].w));//flowmin表示该増广路中拥有最小流量的边的流量
			flow-=flowmin;//当前流量 
			edge[i].w-=flowmin;//每条边的权都减去该增广路中拥有最小流量的边的流量
			flowsum+=flowmin;//要求的总流量 
			edge[i^1].w+=flowmin;//将每条边的反向边的权增加这个值
			if(!flow) break;
		}
	}
    return flowsum;
}
int dinic(){
    
    
    int max_flow = 0;
    while (bfs()){
    
    
        max_flow += dfs(0, inf);
    }
    return max_flow;
}
int main() {
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
    
    
		int t;
		scanf("%d",&t);
		tot+=t;
		addedge(0,i,t);
	}
	
	for(int i=1;i<=n;++i){
    
    
		int t;
		scanf("%d",&t);
		tot+=t;
		addedge(i,n+1,t);
	}
	int m;
	scanf("%d",&m);
	for(int i=1;i<=m;++i){
    
    //继续建边 
		int k,c1,c2;
		scanf("%d%d%d",&k,&c1,&c2);
		tot+=c1+c2;
		addedge(0,i+1+n,c1);//这个组合种在A有c1的收益 
		addedge(i+1+n+m,n+1,c2);//种在B有c2的收益 
		for(int j=1;j<=k;++j){
    
    
			int t;
			scanf("%d",&t);
			addedge(i+1+n,t,inf);
			addedge(t,i+1+m+n,inf);
		}
	} 
	int anse=dinic();
	printf("%d",tot-anse);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45695839/article/details/109480486