洛谷每日一练5.14--P2756+P4014(网络流)

P2756

题意:

给 一 个 二 分 图 要 求 求 出 最 大 匹 配 并 输 出 方 案 给一个二分图要求求出最大匹配并输出方案

思路:

用 最 大 流 解 决 二 分 图 匹 配 问 题 用最大流解决二分图匹配问题
虚 拟 出 源 点 S 和 汇 点 T ; 虚拟出源点S和汇点T; ST;
源 点 S 向 每 个 左 点 连 容 量 为 1 的 边 ; 源点S向每个左点连容量为1的边; S1;
每 个 左 点 向 源 点 S 连 一 条 容 量 为 1 的 反 边 ; 每个左点向源点S连一条容量为1的反边; S1;
每 个 左 点 向 自 己 能 能 匹 配 到 的 右 点 连 容 量 为 1 的 边 ; 每个左点向自己能能匹配到的右点连容量为1的边; 1
每 个 右 点 向 自 己 能 能 匹 配 到 的 左 点 连 容 量 为 0 的 反 边 ; 每个右点向自己能能匹配到的左点连容量为0的反边; 0
每 个 右 点 汇 点 T 连 容 量 为 1 的 边 ; 每个右点汇点T连容量为1的边; T1;
汇 点 T 向 每 个 右 点 连 容 量 为 1 的 边 ; 汇点T向每个右点连容量为1的边; T1;
而 对 于 方 案 , 若 一 条 由 左 点 连 向 右 点 的 边 的 反 向 边 上 有 流 量 则 选 了 而对于方案,若一条由左点连向右点的边的反向边上有流量则选了

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
inline int read(){
	int X = 0,w = 0;char ch = 0;
	while(!isdigit(ch)) {w |= ch == '-';ch = getchar();}
	while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48),ch = getchar();
	return w ? -X : X;
}
const int N = 250;
const int inf = 0x3f3f3f3f;
struct Edge{
	int to,nex,w;
}e[N*N];
int head[N],idx = 1,dep[N];
void add_edge(int u,int v,int w){
	e[++idx].to = v;
	e[idx].w = w;
	e[idx].nex = head[u];
	head[u] = idx;
}

int n,m,S,T;
bool bfs(){
	memset(dep,0,sizeof(dep));
	dep[S] = 1;
	queue<int> q;
	q.push(S);
	while(q.size()){
		int u = q.front();q.pop();
		for(int i = head[u];~i;i = e[i].nex){
			int v = e[i].to,w = e[i].w;
			if(w && !dep[v]){
				dep[v] = dep[u] + 1;
				q.push(v);
			}
		}
	}
	return dep[T];
}
int dfs(int u,int flow){
	if(u == T) return flow;
	int sum = 0;
	for(int i = head[u];~i;i = e[i].nex){
		int v = e[i].to,w = e[i].w;
		if(w && dep[v] == dep[u] + 1){
			int t = dfs(v,min(flow,w));
			flow -= t,sum += t;
			e[i].w -= t,e[i^1].w += t;
		}
	}
	if(!sum) dep[u] = 0;
	return sum;
}
int main(){
	memset(head,-1,sizeof(head));
	scanf("%d%d",&m,&n);
	S = n + 1,T = n + 2;//将源点设为n+1,汇点设为n+2 
	int u,v;
	while(1){
		u = read(),v = read();
		if(u == -1) break;
		add_edge(u,v,1),add_edge(v,u,0); 
	}
	for(int i = 1;i <= m;i++){
		add_edge(S,i,1),add_edge(i,S,0);
	}
	for(int i = m + 1;i <= n;i++){
		add_edge(i,T,1),add_edge(T,i,0);
	}
	int ans = 0;
	while(bfs()) ans += dfs(S,inf);
	printf("%d\n",ans);
	for(int i = 2;i <= idx;i+=2){
		if(e[i].to != S&&e[i^1].to != S)
		if(e[i].to != T&&e[i^1].to != T)
		if(e[i^1].w != 0){
			printf("%d %d\n",e[i^1].to,e[i].to);   	
		}
	}
	return 0;
}

P4014

题意:

给 一 个 二 分 图 , 每 个 匹 配 有 其 价 值 给一个二分图,每个匹配有其价值
求 价 值 最 高 的 完 美 匹 配 和 价 值 最 低 的 完 美 匹 配 求价值最高的完美匹配和价值最低的完美匹配

思路:

费 用 流 , 跑 一 遍 最 小 费 用 最 大 流 和 最 大 费 用 最 大 流 费用流,跑一遍最小费用最大流和最大费用最大流

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
using namespace std;
inline int read(){
	int X = 0,w = 0;char ch = 0;
	while(!isdigit(ch)) {w |= ch == '-';ch = getchar();}
	while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48),ch = getchar();
	return w ? -X : X;
}
const int N = 210,inf = 0x3f3f3f3f;
int n,head[N],dist[N];
int q[N*100],l,r;
int idx,S,T;
int maxflow = 0,mincost = 0;
int mp[N][N];
bool vis[N];
struct node{
    int to,nex,w,cost;
}e[N*N*2];
inline void add(int u,int v,int w,int c) {
    ++idx;
    e[idx].to=v;e[idx].w=w;e[idx].cost=c;e[idx].nex=head[u];head[u]=idx;
    ++idx;
    e[idx].to=u;e[idx].w=0;e[idx].cost=-c;e[idx].nex=head[v];head[v]=idx;
}
bool spfa(){
    memset(vis,0,sizeof(vis));
    memset(dist,0x3f,sizeof(dist));
    l = 1;r = 0;
    queue<int> q;q.push(S);
    dist[S]=0; vis[S]=1;
    while (q.size()) {
        int u = q.front();q.pop();
        vis[u] = 0;
        for (int i = head[u];~i;i = e[i].nex) {
            int v = e[i].to;
            if (e[i].w && dist[v]>dist[u]+e[i].cost) {
                dist[v] = dist[u]+e[i].cost;
                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return dist[T] < inf;
}
int dfs(int u,int flow) {
    if (u == T) {
        vis[T]=1;
        maxflow += flow;
        return flow;
    }
    int goflow = 0,used = 0;
    vis[u]=1;
    for (int i = head[u];~i;i = e[i].nex) {
        int v = e[i].to;
        if ((!vis[v] || v == T) && e[i].w && dist[v]==dist[u]+e[i].cost) {
            goflow = dfs(v,min(flow-used,e[i].w));
            if (!goflow) continue;
            used += goflow;
            e[i].w -= goflow;
            e[i^1].w += goflow;
            mincost += goflow*e[i].cost;
            if (used == flow) break;
        }
    }
    return used;
}
void MCMF(){ 
    while (spfa()) {
        vis[T]=1;
        while (vis[T]) {
            memset(vis,0,sizeof(vis));
            dfs(S,inf);
        }
    }
}
int a=0;
int main(){
	memset(head,-1,sizeof(head));idx = 1;
    n = read();
    S = 0;
    T = 2*n+1;
    for (int i = 1;i <= n;++i) {
        add(S,i,1,0);
        add(i+n,T,1,0);
        for (int j = 1;j <= n;++j) {
            mp[i][j] = read();
            add(i,j+n,1,-mp[i][j]);
        }
    }
    MCMF();
    int mx = -mincost;
    mincost = maxflow = 0;
    idx = 1;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    for (int i = 1;i <= n;++i) {
        add(S,i,1,0);
        add(i+n,T,1,0);
        for (int j = 1;j <= n;++j) {
            add(i,j+n,1,mp[i][j]);
        }
    }
    MCMF();
    printf("%d\n%d\n",mincost,mx);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值