zoukankan      html  css  js  c++  java
  • [题解] PowerOJ 1758 火星探险问题 (最大费用最大流)

    - 传送门 -

     https://www.oj.swust.edu.cn/problem/show/1758

    # 1758: 火星探险问题

    Time Limit: 1000 MS Memory Limit: 65536 KB
    Total Submit: 50 Accepted: 6 Page View: 437

    Description

    火星探险队的登陆舱将在火星表面着陆,登陆舱内有多部障碍物探测车。登陆舱着陆后, 探测车将离开登陆舱向先期到达的传送器方向移动。探测车在移动中还必须采集岩石标本。 每一块岩石标本由最先遇到它的探测车完成采集。每块岩石标本只能被采集一次。岩石标本 被采集后,其他探测车可以从原来岩石标本所在处通过。探测车不能通过有障碍的地面。本 题限定探测车只能从登陆处沿着向南或向东的方向朝传送器移动,而且多个探测车可以在同 一时间占据同一位置。如果某个探测车在到达传送器以前不能继续前进,则该车所采集的岩 石标本将全部损失。 «编程任务: 用一个PxQ 网格表示登陆舱与传送器之间的位置。登陆舱的位置在(X1,Y1)处,传送器 的位置在(XP ,YQ)处。 给定每个位置的状态,计算探测车的最优移动方案,使到达传送器的探测车的数量最多, 而且探测车采集到的岩石标本的数量最多。

    Input

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

    Output

    程序运行结束时,将每个探测车向传送器移动的序列输出到文件output.txt 中。每行包 含探测车号和一个移动方向,0 表示向南移动,1 表示向东移动。

    • Sample Input

    • Raw
      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

    • Sample Output

    • Raw

    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

    Hint

    No SPJ support now!

    Source

    线性规划与网络流24题

     

    - 题意 -

     对于 Q*P 网格, 如果格点内是 1 不能走, 2 表示一个价值为 1 的点, 0 没有特征.
     注意只有第一次到达 2 才能得到价值(如果有多条路径都经过这一点, 只取一次价值).
     现在求出 S(探测车数) 条从(1, 1)(左上) 到 (n, m)(右下) 的路径, 使得到价值最大, 输出每一条路径.
     0 表示向下走, 1 表示向右走.
     

    - 思路 -

     建图什么的就先都连inf的可行边, 再对格点内为 2 的格子多连一条容量 1, 费用 1 的边.
     源点连(1, 1)容量为S, (n,m)连汇点容量为S.
     求最大费用最大流.
     
     重点在输出吧...
     我们只搜索反向边, 反向变 > 0 说明这条边是被经过的.
     因为拆了点, 所以我们固定一下拆出来的一个点集, 记录其内的点以便输出路径.
     
     细节见代码.
     
     PS:
     poweroj没有SPJ挂啦难过!!!
     但是没关系我有dalao写哒!!!
     

    - 代码 -

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int N = 4000;
    const int M = 1e6;
    const int inf = 0x3f3f3f3f;
    
    int NXT[M], TO[M], V[M], CT[M];
    int DIS[N], VIS[N], HD[N], PRE[N];
    int MAP[40][40];
    int ss, tt, n, m, p, sz, tot;
    queue<int> q;
    
    int mk(int i, int j, int z) {
    	return z * tot + (i - 1) * m + j;
    }
    
    void add(int x, int y, int z, int c) {
    	TO[sz] = y; V[sz] = z; CT[sz] = c;
    	NXT[sz] = HD[x]; HD[x] = sz++;
    	TO[sz] = x; V[sz] = 0; CT[sz] = -c;
    	NXT[sz] = HD[y]; HD[y] = sz++;
    }
    
    bool spfa() {
    	memset(DIS, 0xc0, sizeof (DIS));
    	DIS[ss] = 0;
    	PRE[tt] = -1;
    	q.push(ss);
    	while (!q.empty()) {
    		int u = q.front();
    		q.pop();
    		VIS[u] = 0;
    		for (int i = HD[u]; i != -1; i = NXT[i]) {
    			int v = TO[i];
    			if (V[i] && DIS[v] < DIS[u] + CT[i]) {
    				DIS[v] = DIS[u] + CT[i];
    				PRE[v] = i;
    				if (!VIS[v]) {
    					VIS[v] = 1;
    					q.push(v);
    				}
    			}
    		}
    	}
    	return PRE[tt] != -1;
    }
    
    void mcmf() {
    	while (spfa()) {
    		int tmp = inf;
    		for (int i = tt; i != ss; i = TO[PRE[i]^1])
    			if (tmp > V[PRE[i]]) tmp = V[PRE[i]];
    		for (int i = tt; i != ss; i = TO[PRE[i]^1]) {
    			V[PRE[i]] -= tmp;
    			V[PRE[i]^1] += tmp;
    		}
    	}
    }
    
    void bfs(int p, int x, int pre) {
    	if (x != 1 && x <= tot) { //记录x <= tot 的点来标记路径
    		if (x == pre + m)
    			printf("%d 0
    ", p);
    		else
    			printf("%d 1
    ", p);
    		pre = x;
    	}
    	if (x == tot) return;
    	for (int i = HD[x]; i != -1; i = NXT[i]) {
    		if (V[i^1] && i % 2 == 0) { //只搜反向变
    			bfs(p, TO[i], pre);
    			V[i^1] --;
    			break;
    		}
    	}
    }
    
    void print() {
    	for (int i = 1; i <= p; ++i) {
    		bfs(i, 1, 1);
    	}
    }
    
    int main() {
    	memset(HD, -1, sizeof (HD));
    	scanf("%d%d%d", &p, &m, &n);
    	tot = n * m;
    	for (int i = 1; i <= n; ++i)
    		for (int j = 1; j <= m; ++j)
    			scanf("%d", &MAP[i][j]);
    	ss = 0, tt = n * m * 2 + 1;
    	add(ss, mk(1, 1, 0), p, 0);
    	add(mk(n, m, 1), tt, p, 0);
    	for (int i = 1; i <= n; ++i) {
    		for (int j = 1; j <= m; ++j) {
    			if (MAP[i][j] == 1)
    				continue;
    			add(mk(i, j, 0), mk(i, j, 1), inf, 0);
    			if (MAP[i][j] == 2)
    				add(mk(i, j, 0), mk(i, j, 1), 1, 1);
    			if (i != n && MAP[i + 1][j] != 1)
    				add(mk(i, j, 1), mk(i + 1, j, 0), inf, 0);
    			if (j != m && MAP[i][j + 1] != 1)
    				add(mk(i, j, 1), mk(i, j + 1, 0), inf, 0);
    		}
    	}
    	mcmf();
    	print();
    	return 0;
    }
    
  • 相关阅读:
    在HTML网页中嵌入脚本的方式
    纪念品分组(贪心、排序)
    合并果子(STL优先队列)
    铺地毯(取最上层的地毯)
    多项式方程的输出
    BF算法(蛮力匹配)
    数位的处理
    两个数的差
    多项式计算器
    随机数生成器java实现
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7435915.html
Copyright © 2011-2022 走看看