【BZOJ 3049】【USACO2013 Jan】Island Travels BFS+状压DP

这是今天下午的互测题,只得了60多分

分析一下错因:

  $dis[i][j]$只记录了相邻的两个岛屿之间的距离,我一开始以为可以,后来$charge$提醒我有可能会出现来回走的情况,而状压转移就一次,无法实现来回走的情况,所以加了一个类似$floyed算法$的三重循环来更新每个点的距离,然后状态转移就可以了,枚举起点和终点,最后统计答案

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char c[53][53];
int n, m, dis[18][18], belong[53][53], f[40000][18], cnt = 0, qx[2500000], qy[2500000];
int far[2500000], head, tail;
bool vis[53][53];
inline void _(int x, int y) {
	belong[x][y] = cnt;
	vis[x][y] = 1;
	if (x > 1 && c[x - 1][y] == 'X' && !vis[x - 1][y])
		_(x - 1, y);
	if (y > 1 && c[x][y - 1] == 'X' && !vis[x][y - 1])
		_(x, y - 1);
	if (x < n && c[x + 1][y] == 'X' && !vis[x + 1][y])
		_(x + 1, y);
	if (y < m && c[x][y + 1] == 'X' && !vis[x][y + 1])
		_(x, y + 1);
}
inline void __(int x) {
	int nowx, nowy;
	while (head != tail) {
		++head; if ( head >= 2500000) head %= 2500000;
		nowx = qx[head];
		nowy = qy[head];
		if (nowx > 1 && !vis[nowx - 1][nowy] && c[nowx - 1][nowy] != '.') {
			if (c[nowx - 1][nowy] == 'S') {
				++tail; if (tail >= 2500000) tail %= 2500000;
				qx[tail] = nowx - 1;
				qy[tail] = nowy;
				far[tail] = far[head] + 1;
				vis[nowx - 1][nowy] = 1;
			} else {
				dis[x][belong[nowx - 1][nowy]] = min( dis[x][belong[nowx - 1][nowy]], far[head]);
			}
		}
		if (nowy > 1 && !vis[nowx][nowy - 1] && c[nowx][nowy - 1] != '.') {
			if (c[nowx][nowy - 1] == 'S') {
				++tail; if (tail >= 2500000) tail %= 2500000;
				qx[tail] = nowx;
				qy[tail] = nowy - 1;
				far[tail] = far[head] + 1;	
				vis[nowx][nowy - 1] = 1;			
			} else {
				dis[x][belong[nowx][nowy - 1]] = min( dis[x][belong[nowx][nowy - 1]], far[head]);
			}
		}
		if (nowx < n && !vis[nowx + 1][nowy] && c[nowx + 1][nowy] != '.') {
			if (c[nowx + 1][nowy] == 'S') {
				++tail; if (tail >= 2500000) tail %= 2500000;
				qx[tail] = nowx + 1;
				qy[tail] = nowy;
				far[tail] = far[head] + 1;
				vis[nowx + 1][nowy] = 1;
			} else {
				dis[x][belong[nowx + 1][nowy]] = min( dis[x][belong[nowx + 1][nowy]], far[head]);
			}			
		}
		if (nowy < m && !vis[nowx][nowy + 1] && c[nowx][nowy + 1] != '.') {
			if (c[nowx][nowy + 1] == 'S') {
				++tail; if (tail >= 2500000) tail %= 2500000;
				qx[tail] = nowx;
				qy[tail] = nowy + 1;
				far[tail] = far[head] + 1;
				vis[nowx][nowy + 1] = 1;
			} else {
				dis[x][belong[nowx][nowy + 1]] = min( dis[x][belong[nowx][nowy + 1]], far[head]);
			}
		}
	}
}
int main() {
	scanf("%d%d\n", &n, &m);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j) {
			c[i][j] = getchar();
			while (c[i][j] != 'X' && c[i][j] != '.' && c[i][j] != 'S')
				c[i][j] = getchar();
		}
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if (!vis[i][j] && c[i][j] == 'X') {
				++cnt;
				_(i, j);
			}
	memset(dis, 1, sizeof(dis));
	for(int i = 1; i <= cnt; ++i) {
		head = 0;
		tail = 0;
		memset(vis, 0, sizeof(vis));
		for(int k = 1; k <= n; ++k)
			for(int l = 1; l <= m; ++l)
				if (belong[k][l] == i) {
					vis[k][l] = 1;
					++tail; if ( tail >= 2500000) tail %= 2500000;
					qx[tail] = k;
					qy[tail] = l;
					far[tail] = 0;
				}
		__(i);
	}
    for(int k = 1; k <= cnt; ++k)
        for(int i = 1; i <= cnt; ++i)
            for(int j = 1; j <= cnt; ++j)
                if (dis[i][k] + dis[k][j] < dis[i][j])
                    dis[i][j] = dis[i][k] + dis[k][j];
	memset(f, 1, sizeof(f));
	int ans = 500000, tot = (1 << cnt) - 1;
	for(int i = 1; i <= cnt; ++i)
		f[1 << ( i - 1)][i] = 0;
	for(int i = 1; i <= tot; ++i) {
		for(int j = 1; 1 << (j - 1) <= i; ++j) {
			if (1 << (j - 1) & i) {
				for(int k = 1; k <= cnt; ++k)
					if (k != j && (1 << (k - 1) & i))
						f[i][j] = min(f[i][j], f[i ^ (1 << (j - 1))][k] + dis[k][j]);
			}
		}
	}
	for(int i = 1; i <= cnt; ++i)
		ans = min(ans, f[tot][i]);
	printf("%d\n",ans);
	return 0;
}

以后思维得更严谨才行

我的BFS写的就是这么丑,这又怎样?

转载于:https://www.cnblogs.com/abclzr/p/5334265.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一道经典的单调栈问题。题目描述如下: 有 $n$ 个湖,第 $i$ 个湖有一个高度 $h_i$。现在要在这些湖之间挖一些沟渠,使得相邻的湖之间的高度差不超过 $d$。请问最少需要挖多少个沟渠。 这是一道单调栈的典型应用题。我们可以从左到右遍历湖的高度,同时使用一个单调栈来维护之前所有湖的高度。具体来说,我们维护一个单调递增的栈,栈中存储的是湖的下标。假设当前遍历到第 $i$ 个湖,我们需要在之前的湖中找到一个高度最接近 $h_i$ 且高度不超过 $h_i-d$ 的湖,然后从这个湖到第 $i$ 个湖之间挖一条沟渠。具体的实现可以参考下面的代码: ```c++ #include <cstdio> #include <stack> using namespace std; const int N = 100010; int n, d; int h[N]; stack<int> stk; int main() { scanf("%d%d", &n, &d); for (int i = 1; i <= n; i++) scanf("%d", &h[i]); int ans = 0; for (int i = 1; i <= n; i++) { while (!stk.empty() && h[stk.top()] <= h[i] - d) stk.pop(); if (!stk.empty()) ans++; stk.push(i); } printf("%d\n", ans); return 0; } ``` 这里的关键在于,当我们遍历到第 $i$ 个湖时,所有比 $h_i-d$ 小的湖都可以被舍弃,因为它们不可能成为第 $i$ 个湖的前驱。因此,我们可以不断地从栈顶弹出比 $h_i-d$ 小的湖,直到栈顶的湖高度大于 $h_i-d$,然后将 $i$ 入栈。这样,栈中存储的就是当前 $h_i$ 左边所有高度不超过 $h_i-d$ 的湖,栈顶元素就是最靠近 $h_i$ 且高度不超过 $h_i-d$ 的湖。如果栈不为空,说明找到了一个前驱湖,答案加一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值