UVa11419 - SAM I AM(二分图/网络流【坑*方案问题)

题目链接

简介:
子弹打小怪,一发打一行(列),求最小花费的子弹数,并给出方案

分析:
曾经做过和这个有一点相似的题(我又开始考古啦
然而这道题要简单一点,
还是把每一行,每一列看作是一个点,
如果(i,j)位置上有小怪物,那我们就连接Xi—>Yj,流量是INF
源点向横向点集(X)连边,流量为1
纵向点集(Y)向汇点连边,流量是1

为什么这样建图是对的呢:
我们可以把每一条连接X和Y的边看做是小怪物
用最小割的思维,这些边我们都是不会割掉的
所以我们每次只会选择源点和X,Y和汇点之间的连边割掉
这样就可以轻松地得到最小子弹数

但是难点在如何构造合法方案
直接判断是否有流量是不行的

这道题的正解是二分图最大匹配,方案的求解需要匈牙利树
我这是真没招了
看一下dalao的正解

附上第一问的网络流解法

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>

using namespace std;

const int INF=0x33333333;
const int N=1002;
struct node{
    int x,y,v,nxt;
};
node way[N*N*2];
int s,t,st[N*2],tot,deep[N],cur[N],n,m,x;

void add(int u,int w,int z)
{
    tot++;
    way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
    tot++;
    way[tot].x=w;way[tot].y=u;way[tot].v=0;way[tot].nxt=st[w];st[w]=tot;
}

int bfs(int s,int t)
{
    for (int i=s;i<=t;i++) cur[i]=st[i];
    memset(deep,-1,sizeof(deep));
    deep[s]=1;
    queue<int> Q;
    Q.push(s);

    while (!Q.empty())
    {
        int now=Q.front(); Q.pop();
        for (int i=st[now];i!=-1;i=way[i].nxt)
            if (way[i].v&&deep[way[i].y]==-1)
            {
                deep[way[i].y]=deep[now]+1;
                Q.push(way[i].y);
            }
    }

    return deep[t]!=-1; 
}

int dfs(int now,int t,int limit)
{
    if (!limit||now==t) return limit;
    int f,flow=0;
    for (int i=cur[now];i!=-1;i=way[i].nxt)
    {
        cur[now]=i;
        if (way[i].v&&deep[way[i].y]==deep[now]+1&&(f=dfs(way[i].y,t,min(limit,way[i].v))))
        {
            flow+=f;
            limit-=f;
            way[i].v-=f;
            way[i^1].v+=f;
            if (!limit) break;
        }
    }
    return flow;
}

int dinic(int s,int t)
{
    int ans=0;
    while (bfs(s,t))
        ans+=dfs(s,t,INF);
    return ans;
}

int main()
{
    int cnt=0;
    while (scanf("%d%d%d",&n,&m,&x)!=EOF&&n)
    {
        memset(st,-1,sizeof(st));
        tot=-1;
        s=0; t=n+m+1;
        for (int i=1;i<=x;i++)
        {
            int u,w;
            scanf("%d%d",&u,&w);
            add(u,w+n,INF);
        }
        for (int i=1;i<=n;i++) add(s,i,1);
        for (int i=1;i<=m;i++) add(i+n,t,1);

        int ans=dinic(s,t);
        printf("%d\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/wutongtong3117/p/7672999.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值