P3356 火星探险问题

考虑拆点,每个点对 (x,y)(x,y) 拆成入点 uu 和出点 vv。连 xcap=+,cost=0yx \xrightarrow{cap=+\infty,cost=0} y。如果这个地方有石头,再连一条 xcap=1,cost=1yx \xrightarrow{cap=1,cost=1} y,表示石头只能被取一次。对于两个点,从出点连向入点,容量为 ++\infty,费用为 00。跑最大费用最大流。

最后输出方案时,从起点 nn 次搜索,每次从残留网络上反向边容量 >0>0 的地方走并更新容量即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <queue>
using namespace std;

#define int long long

const int N = 5e5 + 5, M = 40;

int n, p, q, ec[M][M];
int e[N], h[N], c[N], cs[N], ne[N], idx, S = 0, T;
int ll[M][M][2];
int nowp = 0;

int dx[] = { 1, 0 };
int dy[] = { 0, 1 };

int dis[N], cur[N];
bool isin[N];

inline int get_in(int x, int y)
{
	return (x - 1) * q + y;
}

inline int get_out(int x, int y)
{
	return get_in(x, y) + p * q;
}

inline void add(int u, int v, int w, int g)
{
	g = -g;
	e[idx] = v, c[idx] = w, cs[idx] = g, ne[idx] = h[u], h[u] = idx++;
	e[idx] = u, c[idx] = 0, cs[idx] = -g, ne[idx] = h[v], h[v] = idx++;
}

inline bool spfa()
{
	for (int i = 0; i <= T; i++) dis[i] = INT_MAX, cur[i] = -1;
	queue<int> q;
	q.push(S);
	dis[S] = 0, cur[S] = h[S];
	while (q.size())
	{
		int u = q.front();
		q.pop();
		isin[u] = 0;
		for (int i = h[u]; ~i; i = ne[i])
		{
			int j = e[i];
			if (dis[j] > dis[u] + cs[i] && c[i] > 0)
			{
				cur[j] = h[j];
				dis[j] = dis[u] + cs[i];
				if (!isin[j])
				{
					q.push(j);
					isin[j] = 1;
				}
			}
		}
	}
	return (dis[T] != INT_MAX);
}

inline int dfs(int u, int lim)
{
	if (u == T) return lim;
	isin[u] = 1;
	int sum = 0;
	for (int i = cur[u]; ~i && sum < lim; i = ne[i])
	{
		cur[u] = i;
		int j = e[i];
		if (dis[j] == dis[u] + cs[i] && c[i] > 0 && !isin[j])
		{
			int p = dfs(j, min(lim - sum, c[i]));
			sum += p;
			c[i] -= p;
			c[i ^ 1] += p;
		}
	}
	isin[u] = 0;
	return sum;
}

int dinic()
{
	int res = 0, p;
	while (spfa())
	{
		while (p = dfs(S, INT_MAX)) res += p;
	}
	return res;
}

void dfss(int x, int y)
{
	if (x == p && y == q)
	{
		return;
	}
	int g = get_out(x, y);
	if (ll[x][y][0] != -1 && c[ll[x][y][0] ^ 1] > 0)
	{
		printf("%d %d\n", nowp, 0);
		c[ll[x][y][0] ^ 1]--;
		dfss(x + dx[0], y + dy[0]);
		return;
	}
	if (ll[x][y][1] != -1 && c[ll[x][y][1] ^ 1] > 0)
	{
		printf("%d %d\n", nowp, 1);
		c[ll[x][y][1] ^ 1]--;
		dfss(x + dx[1], y + dy[1]);
		return;
	}
}

signed main()
{
	memset(h, -1, sizeof h);
	memset(ll, -1, sizeof ll);
	S = 10000, T = 10001;
	scanf("%lld%lld%lld", &n, &q, &p);
	add(S, get_in(1, 1), n, 0);
	add(get_out(p, q), T, n, 0);
	for (int i = 1; i <= p; i++)
	{
		for (int j = 1; j <= q; j++) scanf("%lld", &ec[i][j]);
	}
	if (ec[1][1] == 1 || ec[p][q] == 1) return 0;
	for (int i = 1; i <= p; i++)
	{
		for (int j = 1; j <= q; j++)
		{
			if (ec[i][j] == 1) continue;
			add(get_in(i, j), get_out(i, j), INT_MAX, 0);
			if (ec[i][j] == 2) add(get_in(i, j), get_out(i, j), 1, 1);
			for (int k = 0; k < 2; k++)
			{
				int nx = i + dx[k], ny = j + dy[k];
				if (nx >= 1 && nx <= p && ny >= 1 && ny <= q && ec[nx][ny] != 1)
				{
					add(get_out(i, j), get_in(nx, ny), INT_MAX, 0);
					ll[i][j][k] = h[get_out(i, j)];
				}
			}
		}
	}
	int hh = dinic();
	for (int i = 1; i <= n; i++)
	{
		nowp = i;
		dfss(1, 1);
	}
	return 0;
}
posted @   HappyBobb  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示