洛谷 P3190 [HNOI2007]神奇游乐园 黑 题解

首先不难发现,这是一道插头 \(DP\) 题,因为做到这道题的神犇应该对插头 \(DP\) 有所了解,那么我就简单总结一下插头 \(DP\) 的大致内容。

对于每一行我们用四进制表示其状态,因为四进制方便些。我们对于每一个方块维护一个数 \(num \in \{0,1,2\}\),其中 \(0\) 表示这个块之前没有插头指向它;对于有插头指向的方块,因为最终是个环,所以对于每一行被线穿过的次数必定为偶数(上去了必定要下来),所以我们维护指向该方块的插头是左边的还是右边的,所以有下面 \(7\) 种情况

接下来我们一个一个地思考对应转移方程,直接上图吧

先来看前三种情况

再来看 4、5 两种情况

再看第 6 种情况

最后看第 7 种情况

你会发现第 7 种情况你只能封口(感性理解),此时一个环已经形成,对答案进行更新。

代码如下

#include <iostream>
#include <cstring>

using namespace std;

const int N = 600, M = 107, INF = 0x3f3f3f3f;

int n, m;
int maze[110][10], q[2][N], cnt[2];
int h[2][M], v[2][M];

int find_pos(int cur, int x)
{
	int t = x % M;
	while (h[cur][t] != -1 && h[cur][t] != x)
		if ( ++ t == M) t = 0;
	return t;
}

int get(int state, int k)
{
	return (state >> (k * 2)) & 3;
}

int stt(int k, int v)
{
	return v * (1 << (k * 2));
}

void add(int cur, int state, int w)
{
	int t = find_pos(cur, state);
	if (h[cur][t] == -1)
	{
		h[cur][t] = state;
		v[cur][t] = w;
		q[cur][ ++ cnt[cur]] = t;
	}
	else v[cur][t] = max(v[cur][t], w);
	return;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n >> m;
	for (int i = 1; i <= n; i ++ )
		for (int j = 1; j <= m; j ++ )
			cin >> maze[i][j];
	
	int ans = -INF;
	memset(h, -1, sizeof h);
	int cur = 0;
	add(cur, 0, 0);
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= cnt[cur]; j ++ )
			h[cur][q[cur][j]] <<= 2;
		for (int j = 1; j <= m; j ++ )
		{
			int lst = cur;
			cur ^= 1, cnt[cur] = 0;
			memset(h[cur], -1, sizeof h[cur]);
			for (int k = 1; k <= cnt[lst]; k ++ )
			{
				int state = h[lst][q[lst][k]];
				int w = v[lst][q[lst][k]];
				int x = get(state, j - 1);
				int y = get(state, j);
				if (!x && !y)
				{
					add(cur, state, w);
					if(i < n && j < m) add(cur, state + stt(j - 1, 1) + stt(j, 2), w + maze[i][j]);
				}
				else if (!x && y)
				{
					if (i < n) add(cur, state - stt(j, y) + stt(j - 1, y), w + maze[i][j]);
					if (j < m) add(cur, state, w + maze[i][j]);
				}
				else if (x && !y)
				{
					if (i < n) add(cur, state, w + maze[i][j]);
					if (j < m) add(cur, state - stt(j - 1, x) + stt(j, x), w + maze[i][j]);
				}
				else if (x == 1 && y == 1)
				{
					for (int u = j + 1, s = 1; ; u ++ )
					{
						int z = get(state, u);
						if (z == 1) s ++ ;
						else if (z == 2) s -- ;
						if (s == 0)
						{
							add(cur, state - stt(j - 1, x) - stt(j, y) - stt(u, 1), w + maze[i][j]);
							break;
						}
					}
				}
				else if (x == 2 && y == 2)
				{
					for (int u = j - 2, s = 1; ; u -- )
					{
						int z = get(state, u);
						if (z == 2) s ++ ;
						else if (z == 1) s -- ;
						if (s == 0)
						{
							add(cur, state - stt(j - 1, x) - stt(j, y) + stt(u, 1), w + maze[i][j]);
							break;
						}
					}
				}
				else if (x == 2 && y == 1)
				{
					add(cur, state - stt(j - 1, x) - stt(j, y), w + maze[i][j]);
				}
				else if (x == 1 && y == 2)
				{
					if (state == stt(j - 1, x) + stt(j, y))
						ans = max(ans, w + maze[i][j]);
				}
			}
		}
	}
	
	cout << ans << endl;
	
	return 0;
}
posted @ 2022-08-12 14:17  LittleMoMol  阅读(185)  评论(0编辑  收藏  举报
*/