火星探险问题 网络流

题目描述

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

用一个 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\n", v,d[v],u, d[u], now.cost,v,d[u]+now.cost);
                // printf("\n");
                //printf("%d %d %d %d %d %d\n", u, now.u, now.v, now.c, now.f, now.cost);
                //printf("\n");
                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\n", 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\n", 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\n", 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;
}

原文地址:https://www.cnblogs.com/EchoZQN/p/10797861.html

时间: 2024-10-08 23:51:06

火星探险问题 网络流的相关文章

火星探险问题(最大费用最大流)

火星探险问题(luogu) Solution 容易想到费用流 为解决点权问题,将一个不是障碍的点 i 拆成两个点 ai,bi 从 ai 向 bi 连一条流量为正无穷(表示可以无限次经过),费用为 0(表示这些经过没有收益)的边 若点 i 是石块,则再从 ai 向 bi 连一条流量为1(表示只能经过一次),费用为 1(表示这次经过收益为 1)的边 若点 i 可以到点 j 且 i 和 j 都不是障碍,则从 bi 向 aj 连一条流量为正无穷,费用为 0 的边 从起点向 a1 连一条流量为探测车数,费

【Luogu】P3356火星探险问题(费用流)

题目链接 网络流一条边都不能多连?没道理呀? 不过单看这题的确是个sb题…… #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> #include<cstdlib> #include<queue> #define maxn 100 #define maxm 100000 #define lim n*m #define F(i,j) ((i-1)

[网络流专练6][线性规划与网络流剩余部分题解]

orz"orzGEOTCBRL" 6:lis 给定正整数序列x1 ,…… , xn. (1)计算其最长递增子序列的长度s. (2)计算从给定的序列中最多可取出多少个长度为s的递增子序列. (3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长 度为s的递增子序列.  看到题目就666了,前两问不是经典dp?因为序列是上升的,所以如果在x1前面加一个x1,个数显然多了f[x2][xn][s-1],或者xn后面加一个xn, 一样的……然后发现题意理解错T_T “多

「网络流24题」 题目列表

「网络流24题」 题目列表 序号 题目标题 模型 题解 1 飞行员配对方案问题 二分图最大匹配 <1> 2 太空飞行计划问题 最大权闭合子图 <2> 3 最小路径覆盖问题 二分图最小路径覆盖 <3> 4 魔术球问题 <4> 5 圆桌问题 <5> 6 最长递增子序列问题 <6> 7 试题库问题 <7> 8 机器人路径规划问题 <8> 9 方格取数问题 二分图最大点权独立集 <9> 10 餐巾计划问题

网络流24题小结

网络流24题 前言 网络流的实战应用篇太难做了,因此先完善这一部分 ## 第一题:飞行员配对方案 \(BSOJ2542\)--二分图 最优匹配 题意 两国飞行员\(x\)集合\(y\)集合,\(x\)飞行员可以配对特定的\(y\)集合的飞行员(可无),求一对一配对最大数 Solution 二分图最大匹配裸题,最大流实现 建图:(设\(i\in x\)而\(i'\in y\)) \((S,i,1)~(i',T,1)\) 对\((i,j')\)可匹配\((i,j',1)\) Code 略 ## 第二

《火星救援》观后感

题记 因为对观看电影不怎么感冒,所以我都不知道自己是有多久没有看过电影了.前些天我的笔记本小黑在在熬过了七年的时间后也终于对我说了最后一次的不再见.换新本重装系统的时候看见了后面大老王送给我的英语学习资料--<火星救援>,于是~~~ 责任 <火星救援>开始不久,一场异星球的灾难就如假乱真的在我们眼前铺展开了.灾难的场景的确壮观,但是更吸引我的是队长梅丽莎·刘易斯在那常带来火星救援的沙尘暴中的灾难处理,那是一种艰难的抉择,更是一种不容迟疑的责任.在搜救因沙尘暴导致失联的男一号马克·

网络流24题(好大的坑啊)

不定时更新 飞行员配对方案问题 题解 太空飞行计划问题 最小路径覆盖问题 魔术球问题 圆桌问题 题解 最长递增子序列问题 试题库问题 题解 机器人路径规划问题 方格取数问题 题解 餐巾计划问题 题解 航空路线问题 软件补丁问题 星际转移问题 孤岛营救问题 汽车加油行驶问题 数字梯形问题 运输问题 题解 分配问题 题解 负载平衡问题 题解 深海机器人问题 最长k可重区间集问题 最长k可重线段集问题 火星探险问题 骑士共存问题 原文地址:https://www.cnblogs.com/hbxblog

奥秘月球背面

月球背面 地球和月球之间的潮汐力有效的使月球的自转减缓,使得月球总是以同一面朝向着地球.月球的另一面,从地球上始终不能完全看见,因此被称为月球背面.月球背面的第一张影像是前苏联的月球3号太空船在1959年拍摄的,而人类直到1968年的阿波罗8号任务环绕月球时,才直接用眼睛看见月球背面.背面的地形是一大堆起伏不平的撞击坑,和相对较少的月海.它包含太阳系第二大的撞击坑,南极的南极-艾特肯盆地. Part.1月球 月球,俗称月亮,古称太阴,是环绕地球运行的一颗卫星.它是地球唯一的一颗天然卫星,也是离地

JSOI2012-夏令营 Mar Maps

火星探险 源程序名 MAR.??? (PAS,C,CPP) 输入文件名 MAR.IN 输出文件名     MAR.OUT 时间限制      1S 问题描述: 在2051年,若干火星探险队探索了这颗红色行星的不同的区域并且制作了这些区域的地图.现在, Baltic空间机构有一个雄心勃勃的计划:他们想制作一张整个行星的地图.为了考虑必要的工作,他们需要知道地图上已经存在的全部区域的大小.你的任务是写一个计算这个区域大小的程序. 任务: l         从输入文件 mar.in 读取地图形状的描