题目:
http://acm.hdu.edu.cn/showproblem.php?pid=4796
在图中使用一条线将W和L分在左边和右边,问最小的花费。
思路:
一道插头dp的题目,提前放入从不同的地方出发的状态。注意遇到W和L的时候判断左边插头的数量,及时将必定错误的状态给去掉即可。最后从只有一个插头的状态中获取答案。
代码:
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
int n, m, K, ex, ey;
int mp[25][15];
const int HASH = 30007;
const int STATE = 1000010;
const int inf = 0x3f3f3f3f;
const int mov[14] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26};
inline int BIT(int x, int y) { return ((x) << mov[y]); }
inline int getbit(int x, int y) { return (((x) >> mov[y]) & 3); }
inline int clrbit(int x, int i, int j) { return ((x) & (~(3 << mov[i])) & (~(3 << mov[j]))); }
struct HASHMAP {
int head[HASH], next[STATE], size;
ll state[STATE], f[STATE];
void init() {
size = 0;
memset(head, -1, sizeof(head));
}
void push(ll ky, ll value) {
int h = ky % HASH;
for(int i = head[h]; i != -1; i = next[i]) {
if(state[i] == ky) {
f[i] = min(f[i], value);
return;
}
}
state[size] = ky;
f[size] = value;
next[size] = head[h];
head[h] = size++;
}
}hm[2];
int FindL(int state, int x) {
int cnt = 1;
for(int i = x - 2; i >= 0; i--) {
int tmp = ((state >> mov[i]) & 3);
if(tmp == 2) cnt++;
else if(tmp == 1) cnt--;
if(!cnt) return i;
}
return -1;
}
int FindR(int state, int x) {
int cnt = 1;
for(int i = x + 1; i <= m; i++) {
int tmp = ((state >> mov[i]) & 3);
if(tmp == 1) cnt++;
else if(tmp == 2) cnt--;
if(!cnt) return i;
}
return -1;
}
int FindLs(int state, int x) {
int cnt = 0;
for(int i = x - 2; i >= 0; i--) {
int tmp = ((state >> mov[i]) & 3);
if(tmp > 0) cnt++;
}
return cnt;
}
void dpblank(int i, int j, int cur) {
for(int k = 0; k < hm[cur].size; k++) {
int left = getbit(hm[cur].state[k], j - 1);
int up = getbit(hm[cur].state[k], j);
int s = clrbit(hm[cur].state[k], j - 1, j);
if(!left && !up) {
hm[cur ^ 1].push(s, hm[cur].f[k]);
if(i < (n + 1) && j < m && mp[i + 1][j] >= 0&& mp[i][j + 1] >= 0) {
hm[cur ^ 1].push(s | BIT(1, j - 1) | BIT(2, j), hm[cur].f[k] + mp[i][j]);
}
}
else if(!left || !up) {
int dir = left ? left : up;
if(i < (n + 1) && mp[i + 1][j] >= 0) {
hm[cur ^ 1].push((s | BIT(dir, j - 1)), hm[cur].f[k] + mp[i][j]);
}
if(j < m && mp[i][j + 1] >= 0) hm[cur ^ 1].push((s | BIT(dir, j)), hm[cur].f[k] + mp[i][j]);
}
else if(left == 1 && up == 1) {
hm[cur ^ 1].push(s ^ BIT(3, FindR(s, j)), hm[cur].f[k] + mp[i][j]);
}
else if(left == 2 && up == 2) {
hm[cur ^ 1].push(s ^ BIT(3, FindL(s, j)), hm[cur].f[k] + mp[i][j]);
}
else if(left == 2 && up == 1) {
hm[cur ^ 1].push(s, hm[cur].f[k] + mp[i][j]);
}
else if(left == 3 && up == 1) {
hm[cur ^ 1].push(s | BIT(3, FindR(s, j)), hm[cur].f[k] + mp[i][j]);
}
else if(left == 2 && up == 3) {
hm[cur ^ 1].push(s | BIT(3, FindL(s, j)), hm[cur].f[k] + mp[i][j]);
}
}
}
void dpblock(int i, int j, int cur) {
for(int k = 0; k < hm[cur].size; k++) {
if(mp[i][j] == -3) {
int cnt = FindLs(hm[cur].state[k], j);
if(cnt & 1) continue;
}
if(mp[i][j] == -2) {
int cnt = FindLs(hm[cur].state[k], j);
if(cnt % 2 == 0) continue;
}
int s = clrbit(hm[cur].state[k], j - 1, j);
hm[cur ^ 1].push(s, hm[cur].f[k]);
}
}
ll solve() {
int cur = 0; hm[0].init();
for(int i = 1; i <= m; i++) {
hm[cur].push((0 | BIT(3, i - 1)), 0);
}
for(int i = 1; i <= n; i++) {
cur ^= 1;
hm[cur].init();
for(int j = 0; j < hm[cur ^ 1].size; j++) {
int s = (hm[cur ^ 1].state[j] << 2);
hm[cur].push(s, hm[cur ^ 1].f[j]);
}
//shift
for(int j = 1; j <= m; j++) {
hm[cur ^ 1].init();
if(mp[i][j] >= 0) dpblank(i, j, cur);
else dpblock(i, j, cur);
cur ^= 1;
}
}
ll ans = inf;
for(int i = 0; i < hm[cur].size; i++) {
int cnt = 0;
for(int j = 0; j <= m; j++) {
int tmp = getbit(hm[cur].state[i], j);
if(tmp > 0)
cnt++;
}
if(cnt == 1)
ans = min(ans, hm[cur].f[i]);
}
if(ans != inf) return ans;
else return -1;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
memset(mp, -1, sizeof(mp));
char str[25];
for(int i = 1; i <= n; i++) {
scanf("%s", str + 1);
for(int j = 1; j <= m; j++)
if(str[j] == '#') mp[i][j] = -1;
else if(str[j] == 'L') mp[i][j] = -2;
else if(str[j] == 'W') mp[i][j] = -3;
else mp[i][j] = str[j] - '0';
}
for(int j = 1; j <= m; j++) {
mp[n + 1][j] = 0;
}
printf("%lld\n", solve());
}
return 0;
}