[HDU 4796][ZOJ 3731] Winter's Coming

题目:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值