HDU2732Leapin' Lizards(最大流+拆点)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2732

题意:

       给你一个网格,网格上的一些位置上有一只蜥蜴,所有蜥蜴的最大跳跃距离是d,如果一只蜥蜴能跳出网格边缘,那么它就安全了.且每个网格有一个最大跳出次数x,即最多有x只蜥蜴从这个网格跳出,这个网格就再也不能有蜥蜴进来了.问你最少有多少只蜥蜴跳不出网格.


输入描述:输入矩阵的行M和跳跃值D,接着输入两个矩阵(列数自己求),第一个矩阵是每个柱子的耐久值,下面一个矩阵有'L'的表示这个柱子上有一只蜥蜴。


给出来自大神的分析:传送门http://blog.csdn.net/u013480600/article/details/38964749

分析:

 建图:

       源点S编号0,网格的每个格子分成两个点i和i+n*m(nm为网格的行和列数,其实i编号点是表示蜥蜴进来,i+n*m编号的点是表示蜥蜴出去).汇点t编号n*m*2+1.

       如果格子i上有蜥蜴,那么从s到i有边(s,i,1).

       如果格子i能承受x次跳出,那么有边(i,i+n*m,x)

       如果从格子i能直接跳出网格边界,那么有边(i+n*m,t,INF)

       如果从格子i不能直接跳出网格,那么从i到离i距离<=d的网格j有边(i+n*m,j,INF). 注意这里的距离是abs(行号之差)+abs(列号之差)

       最终我们求出的最大流就是能跳出网格的蜥蜴数.

       原题中提到:任意时刻每个柱子上最多只有1只蜥蜴在上面,那么我们上面的解法会不会与这个要求冲突呢?

       不会的,假设有k只蜥蜴能出去,那么一定存在一个符合上面要求的解,使得这k只蜥蜴按顺序出去,在任意时刻每个柱子上最多只有1只蜥蜴.(假设在某个时刻,蜥蜴j想出去,它跳到了柱子h上,但是柱子h上已经有蜥蜴了,那么这样就违反了上面的要求. 其实我们可以这么想,我们为什么不让柱子h上的蜥蜴先按照蜥蜴j以前的逃跑路线出去,然后再让蜥蜴j到柱子h上去替代之前的蜥蜴,那么这样既不违反规则,也得到了解)


收获:

1.这是第一次接触接触到拆点这个技巧。本题中对于那些不能跳出去但是又有柱子的点,那么我们就去按照跳跃距离搜寻有没有其他的柱子能够去跳跃,如果能够找到的话,那么连接这两点,并且将容量控制为弧尾节点的柱子耐久度,这是因为一条弧只能约束一个顶点。如果这个柱子可以跳到旁边很多的点上,每一个边的容量都是这个限定次数的话,等于限定的次数被放大了,所以我们需要进行拆点,点内之间的流量为该柱子本身的耐久度。

2.上面提到最多只有1只蜥蜴在柱子上,但是思考之后,用水是能够流动的思想来思考的话,其实这并不会影响求解的。就像HDU6017一样,思考交换的是哪个2并不重要。


AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define PI acos(-1.0)
#define eps 1e-8
#define pb push_back
#define mp make_pair
#define MII map<int,int>::iterator
#define MLL map<LL,LL>::iterator
#define pii pair<int,int>
#define SI set<int>::iterator
#define SL set<LL>::iterator
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
#define bug printf("bug-------bug-------bug\n")
using namespace std;
typedef long long ll;
typedef unsigned long long ul;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }

const int maxn = 1000;
const int INF = 0x3f3f3f3f;

struct Edge
{
    int from, to, cap, flow;
    Edge(){}
    Edge(int _from, int _to, int _cap, int _flow):from(_from), to(_to), cap(_cap), flow(_flow){}
};

struct Dinic
{
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[maxn];
    int cur[maxn];
    int d[maxn];
    bool vis[maxn];
    void init(int n, int s, int t)
    {
        this->n = n;this->s = s;this->t = t;
        edges.clear();
        for(int i = 0; i < n; i++)
            G[i].clear();
    }
    void AddEdge(int from, int to, int cap)
    {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BFS()
    {
        queue<int> q;
        q.push(s);
        memset(vis, false, sizeof(vis));
        d[s] = 0;
        vis[s] = true;
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            for(int i = 0; i < G[u].size(); i++)
            {
                Edge &e = edges[G[u][i]];
                if(!vis[e.to] && e.cap > e.flow)
                {
                    d[e.to] = d[u] + 1;
                    vis[e.to] = true;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x, int maxflow)
    {
        if(x == t || maxflow == 0)
            return maxflow;
        int flow = 0, f;
        for(int& i = cur[x]; i < G[x].size(); i++)
        {
            Edge &e = edges[G[x][i]];
            if(d[e.to] == d[x] + 1 && (f = DFS(e.to, min(maxflow, e.cap-e.flow) ) ) > 0)
            {
                e.flow += f;
                edges[G[x][i]^1].flow -= f;
                flow += f;
                maxflow -= f;
                if(maxflow == 0)
                    break;
            }
        }
        return flow;
    }
    int max_flow()
    {
        int ans = 0;
        while(BFS())
        {
            memset(cur, 0, sizeof(cur));
            ans += DFS(s, INF);
        }
        return ans;
    }
}DC;
int main()
{
    int T;scanf("%d", &T);
    for(int kase = 1; kase <= T; kase++)
    {
        int n, m, d, src, dst;
        int sum = 0;
        scanf("%d%d", &n, &d);
        for(int i = 1; i <= n; i++)
        {
            string s;
            cin >> s;
            if(i == 1)
            {
                m = s.size();
                src = 0;
                dst = 2*n*m+1;
                DC.init(2*n*m+2, src, dst);
            }
            for(int j = 0; j < s.size(); j++)if(s[j]-'0' > 0)
            {
                int id = (i-1)*m + j + 1;
                DC.AddEdge(id, id+n*m, s[j]-'0');
                if(i <= d || i+d > n || j < d || j+d >= m)
                    DC.AddEdge(id+n*m, dst, INF);
                else
                {
                    for(int k = 1; k <= n; k++)
                        for(int h = 0; h < m; h++)
                        {
                            int id2 = (k-1)*m + h + 1;
                            if(id == id2)
                                continue;
                            if(abs(i-k) + abs(j-h) <= d)
                                DC.AddEdge(id+n*m, id2, INF);
                        }
                }
            }
        }
        for(int i = 1; i <= n; i++)
        {
            string s;
            cin >> s;
            for(int j = 0; j < s.size(); j++)
            {
                if(s[j] == 'L')
                {
                    int id = (i-1)*m+j+1;
                    sum++;
                    DC.AddEdge(src, id, 1);
                }
            }
        }
        int ans = sum - DC.max_flow();
        if(ans == 0) printf("Case #%d: no lizard was left behind.\n", kase);
        else if(ans == 1) printf("Case #%d: 1 lizard was left behind.\n", kase);
        else printf("Case #%d: %d lizards were left behind.\n", kase, ans);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值