zoukankan      html  css  js  c++  java
  • 火星探险问题 网络流

    题目描述

    火星探险队的登陆舱将在火星表面着陆,登陆舱内有多部障碍物探测车。登陆舱着陆后,探测车将离开登陆舱向先期到达的传送器方向移动。探测车在移动中还必须采集岩石标本。每一块岩石标本由最先遇到它的探测车完成采集。每块岩石标本只能被采集一次。岩石标本被采集后,其他探测车可以从原来岩石标本所在处通过。探测车不能通过有障碍的地面。本题限定探测车只能从登陆处沿着向南或向东的方向朝传送器移动,而且多个探测车可以在同一时间占据同一位置。如果某个探测车在到达传送器以前不能继续前进,则该车所采集的岩石标本将全部损失。

    用一个 P·Q 网格表示登陆舱与传送器之间的位置。登陆舱的位置在(X1,Y1)处,传送器

    的位置在(XP ,YQ)处。

    X 1,Y 1 X 2 , Y 1 X 3 , Y 1 ... X P-1, Y 1 X P , Y 1

    X 1,Y 2 X 2 , Y 2 X 3 , Y 2 ... X P-1, Y 2 X P , Y 2

    X 1, Y 3 X 2 , Y 3 X 3 ,Y 3 ... X P-1, Y 3 X P , Y 3

    ... ...

    X 1 ,Y Q-1 X 2 , Y Q-1 X 3 , Y Q-1 ... X P-1, Y Q-1 X P , Y Q-1

    X 1,Y Q X 2 , Y Q X 3 , Y Q ... X P-1, Y Q X P ,Y Q

    给定每个位置的状态,计算探测车的最优移动方案,使到达传送器的探测车的数量最多,

    而且探测车采集到的岩石标本的数量最多

    输入输出格式

    输入格式:

    第 1行为探测车数,第 2 行为 P 的值,第3 行为Q 的值。接下来的 Q 行是表示登陆舱与传送器之间的位置状态的 P·Q 网格。用 3 个数字表示火星表面位置的状态:0 表示平坦无障碍,1表示障碍,2 表示石块。

    输出格式:

    每行包含探测车号和一个移动方向,0 表示向南移动,1 表示向东移动。

    输入输出样例

    输入样例#1: 复制
    2
    10
    8
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 1 1 0 0 0
    0 0 0 1 0 2 0 0 0 0
    1 1 0 1 2 0 0 0 0 1
    0 1 0 0 2 0 1 1 0 0
    0 1 0 1 0 0 1 1 0 0
    0 1 2 0 0 0 0 1 0 0
    0 0 0 0 0 0 0 0 0 0
    输出样例#1: 复制
    1 1
    1 1
    1 1
    1 1
    1 0
    1 0
    1 1
    1 1
    1 1
    1 1
    1 0
    1 0
    1 1
    1 0
    1 0
    1 0
    2 1
    2 1
    2 1
    2 1
    2 0
    2 0
    2 0
    2 0
    2 1
    2 0
    2 0
    2 1
    2 0
    2 1
    2 1
    2 1

    说明

    车数,P,Q<=35

    这个题目和深海机器人很像,思路差不多,就是完全按照这个坐标轴建图就可以了。

    不过有几个地方要注意的,一个就是建图需要用拆点,因为这个题目给的是点权,而深海机器人给的是边权,

    如果一个题目经过一个点的次数有限制,那么就需要进行拆点,如果题目给的是边权就不需要,这个是为什么呢?

    这个你可以看一下下面这个牛吃草问题,应该可以理解。

    除了这个还有就是路径的输出,你需要理解一下,很容易就可以发现,这个路径数就是最大流,

    还有就是每一个点有没有被经过,就只要看它的流量是不是>0,如果大于0,就是被经过了,否则就是没有被经过。

    这个路径的输出其实很好理解。

    #include <cstdio>
    #include <cstdlib>
    #include <queue>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <map>
    #include <cstring>
    #include <cmath>
    #include <string>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int INF = 0x3f3f3f3f;
    const int maxn = 1e5;
    struct edge
    {
        int u, v, c, f, cost;
        edge(int u, int v, int c, int f, int cost) :u(u), v(v), c(c), f(f), cost(cost) {}
    };
    vector<edge>e;
    vector<int>G[maxn];
    int a[maxn];//找增广路每个点的水流量
    int p[maxn];//每次找增广路反向记录路径
    int d[maxn];//SPFA算法的最短路
    int inq[maxn];//SPFA算法是否在队列中
    int s, t;
    void init()
    {
        for (int i = 0; i <=maxn; i++)G[i].clear();
        e.clear();
    }
    void add(int u, int v, int c, int cost)
    {
        e.push_back(edge(u, v, c, 0, cost));
        e.push_back(edge(v, u, 0, 0, -cost));
        int m = e.size();
        G[u].push_back(m - 2);
        G[v].push_back(m - 1);
    }
    bool bellman(int s, int t, int& flow, long long & cost)
    {
        memset(d, inf, sizeof(d));
        memset(inq, 0, sizeof(inq));
        d[s] = 0; inq[s] = 1;//源点s的距离设为0,标记入队
        p[s] = 0; a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的)
    
        queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流
        q.push(s);
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            inq[u] = 0;//入队列标记删除
            for (int i = 0; i < G[u].size(); i++)
            {
                edge & now = e[G[u][i]];
                int v = now.v;
                if (now.c > now.f && d[v] > d[u] + now.cost)
                    //now.c > now.f表示这条路还未流满(和最大流一样)
                    //d[v] > d[u] + e.cost Bellman 算法中边的松弛
                {
                    // printf("d[%d]=%d d[%d]=%d %d d[%d]=%d
    ", v,d[v],u, d[u], now.cost,v,d[u]+now.cost);
                    // printf("
    ");
                    //printf("%d %d %d %d %d %d
    ", u, now.u, now.v, now.c, now.f, now.cost);
                    //printf("
    ");
                    d[v] = d[u] + now.cost;//Bellman 算法边的松弛
                    p[v] = G[u][i];//反向记录边的编号
                    a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量
                    if (!inq[v]) { q.push(v); inq[v] = 1; }//Bellman 算法入队
                }
            }
        }
        if (d[t] == inf)return false;//找不到增广路
        flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow
        cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用
        //printf("%d %d
    ", d[t], a[t]);
        for (int u = t; u != s; u = e[p[u]].u)//逆向存边
        {
            e[p[u]].f += a[t];//正向边加上流量
            e[p[u] ^ 1].f -= a[t];//反向边减去流量 (和增广路算法一样)
            //printf("e[%d]=%d e[%d]=%d
    ", p[u], e[p[u]].f, p[u] ^ 1, e[p[u] ^ 1].f);
        }
        return true;
    }
    int MaxcostMaxflow(int s, int t, long long & cost)
    {
        cost = 0;
        int flow = 0;
        while (bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost
        return flow;//返回最大流,cost引用可以直接返回最小费用
    }
    int mp[110][110];
    int mpp[110][110];
    int n, m;
    void dfs(int x,int y,int u,int k)
    {
        for(int i=0;i<G[u].size();i++)
        {
            int kx, ky, mov;
            edge &now = e[G[u][i]];
            if (now.v == s ||  now.v == u - n * m) continue;
            if (now.v == t) continue;
            if (now.f == 0) continue;
            now.f--;
            if(now.v>n*m)
            {
                dfs(x, y, now.v, k);
                return;
            }
            if(mpp[x][y]+1==now.v)
            {
                kx = x;
                ky = y + 1;
                mov = 1;
            }
            else
            {
                mov = 0;
                ky = y;
                kx = x + 1;
            }
            printf("%d %d
    ", k, mov);
            dfs(kx, ky, now.v+n*m, k);
            return;
        }
    }
    
    int main()
    {
        int id = 1, k;
        cin >> k >> m >> n;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                cin >> mp[i][j];
                mpp[i][j] = id++;
            }
        }
        int s = 0, t = 2 * n * m + 1;
        if (mp[1][1] != 1) add(s, mpp[1][1], k, 0);
        if (mp[n][m] != 1) add(mpp[n][m] + n * m, t, k, 0);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if (mp[i][j] == 1) continue;
                add(mpp[i][j], mpp[i][j] + n * m, inf, 0);
                if (mp[i][j] == 2)
                {
                    add(mpp[i][j], mpp[i][j] + n * m, 1, -1);
                }
                if(i!=1&&mp[i-1][j]!=1)
                {
                    add(mpp[i - 1][j] + n * m, mpp[i][j], inf, 0);
                }
                if(j!=1&&mp[i][j-1]!=1)
                {
                    add(mpp[i][j - 1] + n * m, mpp[i][j], inf, 0);
                }
            }
        }
        ll cost = 0;
        int ans = MaxcostMaxflow(s, t, cost);
        for(int i=1;i<=ans;i++)
        {
            dfs(1, 1, 1, i);
        }
        return 0;
    }
  • 相关阅读:
    Get-CrmSetting返回Unable to connect to the remote server的解决办法
    Dynamics 365中的常用Associate和Disassociate消息汇总
    Dynamics 365 Customer Engagement V9 活动源功能报错的解决方法
    Dynamics Customer Engagement V9版本配置面向Internet的部署时候下一步按钮不可点击的解决办法
    Dynamics 365检查工作流、SDK插件步骤是否选中运行成功后自动删除系统作业记录
    注意,更改团队所属业务部门用Update消息无效!
    Dynamics 365的审核日志分区删除超时报错怎么办?
    Dynamics 365使用Execute Multiple Request删除系统作业实体记录
    Dynamics 365的系统作业实体记录增长太快怎么回事?
    Dynamics CRM日期字段查询使用时分秒的方法
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/10797861.html
Copyright © 2011-2022 走看看