首页 > ACM题库 > HDU-杭电 > HDU 4796-Winter’s Coming-动态规划-[解题报告]HOJ
2015
09-18

HDU 4796-Winter’s Coming-动态规划-[解题报告]HOJ

Winter’s Coming

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 55    Accepted Submission(s): 9



Problem Description
″You are too young to be burdened with all my cares,″ the father told her, ″but you are also a Stark of Winterfell. You know our words.″
″Winter is coming,″ Arya whispered, thinking of Nymeria. She hugged her knees against her chest, suddenly afraid.
″Remember the sigil of our House, Arya. Let me tell you something about it. When the snows fall and the white winds blow, the lone Wolf dies, but the pack survives. Summer is the time for squabbles. In winter, we must protect one another, keep each other warm,
share our strengths. So if you must hate, Arya, hate those who would truly do us harm. Sansa… Sansa is your sister. You may be as different as the sun and the moon, but the same blood flows through both your hearts. You need her, as she needs you… and
I need both of you, gods help me. Gods commanded us to build a long wall, which is regardless of the thickness, to defend the attack from the Lannister. Now we should carry out the wall.″ Ned Stark sounded so tired that it made Arya sad.
″I do not mean to frighten you, but neither will I lie to you. We have come to a dark dangerous place, child. We have enemies who mean us ill in the King’s Landing. We’ll point the swords to the Lannister, rather than fight a war among ourselves. Our construction
crew will build a wall which connect the north border and south border, separate the mainland into exact two parts. All our Wolf’s cities should lie on the left part, while All Lannister’s cities lie on the right part. Precisely, the wall can’t pass through
a grid more than once, and should run parallel or vertically to the four borders. With winter soon upon us, that is a different matter that we should minimize the time cost.″
″How much time do we have, roughly?″ Arya take out the draft and has been ready to calculate.
″Winter is coming.″ Her father frowned.
Winter's Coming
 


Input
There are multiple cases. The first line of each case contains two integers, N, M (1 ≤ N ≤ 20, 1 ≤ M ≤ 10), indicating the width and length of the mainland’s layout.
In the map, the character is ‘W’ indicated the Wolf’s city, ‘L’ indicated the Lannister’s city, ‘#’ means a grid where forbade any construction, and a number in ’0′-’9′ indicated the time cost to build a wall on this grid.
 


Output
For each case, output one integer, the minimum time cost to build a valid wall, or -1 if we can’t work out an approach.
 


Sample Input
6 8 88W888L8 888#W888 888888L8 8W88L#88 8888888L 88888W88
 


Sample Output
88
 


Source

题意:有一个N*M的方格图,你需要在上面修建一道墙到下面,使得’W'全部在墙的左边区域,‘L’全部在墙的右边区域,每个格子只能建一次墙,有些格子不能建墙,每个格子建墙有对应的花费。问最少花费是多少。


思路:

    我是第一次做插头dp,以前都只是yy过,大概明白了他是根据什么来进行转移的,他其实就是根据轮廓线上的“状态”来进行转移的,一开始的时候都不知道轮廓线是什么,状态是怎么表示的,看别人的东西都写得好玄乎,根本不知道在说什么。

    我简单的讲讲这些“术语”说的是什么,先说说轮廓线。

Winter's ComingWinter's Coming

    看这个图,其中那条蓝色的线就是我们所说的“轮廓线”,我们能看出这条轮廓线横向的经过了所有列,竖向的经过一个格子。我们转移的过程中,这个轮廓线会进行变化,会怎么变呢,请看图中红色的线是转移后的轮廓线,其实就是竖向的往右移动了一个格子。 所以我们的转移是一个格子一个格子的转移。

    再说说状态是什么。我们能看出图中的红线和蓝线都经过了7个格子的边缘线。假设我们用1代表轮廓线所在边缘线有一条黑线穿过这个轮廓线,0代表没有,那么我们能用7个状态位来表示这个轮廓线,看蓝色的线,从左到右的看,它的状态就是1110111。再看红线,它的状态是1110001。 我们可以把它看成一个二进制数。

Winter's ComingWinter's Coming

    这基本就是插头dp里面状态定义和转移方法了,不懂的话…….以后会自己想明白的。

    不过我的代码里面那条竖线的状态我是放在最后一位的。 所以dp的状态是dp[i][j][k], 表示当前位置在(i,j) (这个当前位置就是那个竖线左边的格子),轮廓线状态为k。基础知识讲到这里吧。


    接下来讲讲这个题目。做这个题目之前可以先看一下《基于连通性状态压缩的动态规划问题》,里面介绍了一个与这道题相近的题目,可以直接用里面的思想来做这道题。 本题里不需要每一个格子都建墙,只需要从第一行到达最后一行就行了。 我们的状态仍然可以用4进制,状态表示依然是
0- 空位插头 , 1-左括号插头, 2 – 右括号插头, 3-独立插头。  那么我们一开始的初始化就是从-1行选一列附上状态3,其他是0就行了。 然后一格一格的进行转移。分类讨论一下就行了,由于我是第一次写插头dp,我就具体说一下是怎么分类的。

每次转移的时候我们一般先关注一个格子会怎么变。

(1). 如果左边和上边的状态都是0,那么这个格子不可能有往上或者往左的直线,所以要么没有直线,要么是从下到右有一条直线,所以状态转移到(0,0) 或者是(1,2), 注意其他位置是不会变的,不用考虑。

(2). 如果左边和上边的状态有一个是0, 那么表示有一条线从左边或者是上边进来,然后必须要从下边或者右边出去,那么能够转移到的状态就是(x,0) 或者(0,x) 其中x 就是上边和左边的状态中不为0的状态值。

(3). 如果左边和上边是 (2,1) , 那么上边和左边有一条直线进来,那么它们一定是要是这样子的,所以下边和右边的状态是(0,0)

(4).如果左边和上边是(1,2),注意这时候理论上格子也是上一种情况的样子,但是这种转移是不允许的,因为这样的话会独立的一个环,想想为什么?

(5).如果左边和上边的状态一样,(注意,一个状态里面有且仅有一个3),那么格子情况一样,但是别的地方的状态会改变,如果都是1,那么轮廓线状态中右边的第一个2要变成1,因为原来的是’ ( ( ) )’ ,而这里把他们接上了,那么就变成了’##()’了,
如果都是2,类似的。

(6).如果左边状态是3,那么上边是1 (2不可能,0讨论过),这时候格子依然跟上面一样。此时,轮廓线右边第一个2就要变成3了。

(7).左边2,上边3,跟6类似,反过来而已。

    差不多是这样吧,还有就是有些格子不能建墙,要判断一下,有些‘W’,’L’的格子怎么判断是不是在左边和右边呢?我们只需要看轮廓线左边不是0状态的个数是偶数个还是奇数个就行了,偶数个在左边,奇数个在右边,因为从左边走到右边,每碰到一面墙就要变一次“左右"。还有就是最后一列转移要注意竖线的状态位只能是0. 

    这道题基本就是这样了。。。。反正写起来好麻烦的样子。具体请看代码。

    写了一大段…..如果有错别字请多多包涵……

代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string.h>
#include <cstring>
#include <time.h>
#include <stdio.h>
#include <queue>
#include <cmath>
#include <set>
#include <math.h>
#include <map>
#define rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,b,a) for(int i = (b); i >= (a); --i)
#define clr(a,x) memset(a,(x),sizeof(a))
#define LL long long
#define eps 1e-10
#define zero(x) -eps < (x) && (x) < eps
using namespace std;
short dp[2][1<<22];
int q[2][1<<15];
bool inq[2][1<<22];
const int inf = 1<<14;
char A[33][33];
int N,M;

inline int min(int a,int b) { return a < b ? a : b; }
inline int max(int a,int b) { return a > b ? a : b; }

inline int getBit(int s,int p)
{
    return (s & (3 << (p << 1))) >> (p << 1);
}

inline void setBit(int & s,int p,int x)
{
    s = (s ^ (s & (3 << (p<<1)))) | (x << (p<<1));
}

int cur;
int rear[2];

inline void update(int r,int c,int t,int cost)
{
    if (dp[cur^1][t] > cost) dp[cur^1][t] = cost;
    if (!inq[cur^1][t]) inq[cur^1][t] = true, q[cur^1][rear[cur^1]++] = t;
}

inline void dp_block(int r,int c,int s)
{
    int t, c1 = getBit(s,M), c2 = getBit(s,c);
    if (c1 || c2) return;

    if (A[r][c] == '#') {
        t = s;
        update(r,c,t,dp[cur][s]);
        return;
    }
    int side = 0;
    rep(j,0,c)
    if (getBit(s,j)) side ^= 1;
    if (side == 0 && A[r][c] == 'L') return;
    if (side == 1 && A[r][c] == 'W') return;
    t = s;
    update(r,c,t,dp[cur][s]);
}

inline void dp_digit(int r,int c,int s)
{
    int t;
    int c1 = getBit(s,M);
    int c2 = getBit(s,c);
    if (c1 == 0 && c2 == 0) {
        t = s;
        update(r,c,t,dp[cur][s]);
        setBit(t,M,2); setBit(t,c,1);
        if (c < M-1) update(r,c,t,dp[cur][s] + A[r][c] - '0');
    } else if (c1 == 0 || c2 == 0) {
        int x = (c1 == 0 ? c2 : c1);
        t = s;
        setBit(t,M,x); setBit(t,c,0);
        if (c < M-1) update(r,c,t,dp[cur][s] + A[r][c] - '0');

        t = s;
        setBit(t,c,x); setBit(t,M,0);
        update(r,c,t,dp[cur][s] + A[r][c] - '0');
    } else if (c1 == c2) {
        int z = (c1 == 1 ? c : c-1);
        for(; 0 <= z && z < M; z += (c1 == 1 ? 1 : -1))
        if (getBit(s,z) == (c1 ^ 3)) break;
        if (0 <= z && z < M) {
            t = s;
            setBit(t,z,c1);
            setBit(t,M,0); setBit(t,c,0);
            update(r,c,t,dp[cur][s] + A[r][c] - '0');
        }
    } else if (c1 == 2 && c2 == 1) {
        t = s; setBit(t,M,0); setBit(t,c,0);
        update(r,c,t,dp[cur][s] + A[r][c] - '0');
    } else if (c1 == 3 && c2 == 1) {
        int z = c;
        for(; z < M; ++z)
        if (getBit(s,z) == 2) break;
        if (z < M) {
            t = s;
            setBit(t,c,0); setBit(t,M,0);
            setBit(t,z,3);
            update(r,c,t,dp[cur][s] + A[r][c] - '0');
        }
    } else if (c1 == 2 && c2 == 3) {
        int z = c-1;
        for(; z >= 0; --z)
        if (getBit(s,z) == 1) break;
        if (z >= 0) {
            t = s;
            setBit(t,c,0); setBit(t,M,0);
            setBit(t,z,3);
            update(r,c,t,dp[cur][s] + A[r][c] - '0');
        }
    }
}

void solve()
{
    cur = 0;
    rear[0] = rear[1] = 0;
    rep(i,0,(1<<((M+1)<<1))) dp[0][i] = dp[1][i] = inf;
    clr(inq,0);
    rep(i,0,M) {
        int s = 0;
        setBit(s,i,3);
        dp[cur][s] = 0;
        q[cur][rear[cur]++] = s;
    }
  //  int maxsize = 0;
    rep(i,0,N) rep(j,0,M) {
        rep(k,0,rear[cur]) {
            int s = q[cur][k];
            if (isdigit(A[i][j])) dp_digit(i,j,s);
            else dp_block(i,j,s);
            inq[cur][s] = false;
            dp[cur][s] = inf;
        }
        rear[cur] = 0;
        cur ^= 1;
    }
    int ans = inf;
    int c = 0;
    rep(j,0,M) {
        int s = 0;
        setBit(s,j,3);
        ans = min(ans,dp[cur][s]);
    }
    if (ans == inf) ans = -1;
    printf("%d\n",ans);
}


int main()
{
    #ifdef ACM
        freopen("in.txt", "r", stdin);
    #endif // AC
    while (scanf("%d%d",&N,&M)==2) {
        rep(i,0,N) scanf("%s",A[i]);
        int W = 0, L = 0;
        rep(i,0,N) rep(j,0,M)
        if (A[i][j] == 'W') ++W;
        else if (A[i][j] == 'L') ++L;
      //  while (W == 0 && L == 0);
        solve();
    }
}




参考:http://blog.csdn.net/wsx1754175/article/details/41876791