洛谷 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;
}

插头DP简单应用,很有意思
浙公网安备 33010602011771号