[ACM] POJ1185解题报告 状态压缩DP

思路和网上的都差不多, 我另外开了程序算出每一行可能的情况直接copy进 stack[] 中..耍流氓.. 看起来直观点
引用一下大牛BYVoid的思路, 不自己写了

较难看出的动态规划问题。注意到数据范围N≤100;M≤10,发现每行的宽度都不大,所以可以考虑把一行看成一个状态,表示该行的布置方案。每行的布置方案可以预先处理出,由数学知识可以算出,每行最多的布置方案数也不过60个而已。

状态设定

F[i,a,b]为前i行,第i行的方案为A[a],第i-1行的方案为A[b]的最大炮兵数。

状态转移方程

  • F[i,a,b]=Max{ F[i-1,b,c] + P[a] }

其中c为i-2行的布置方案,a,b,c应保证互不冲突,即放置方案中炮兵都在平地上,且不会互相攻击到。

目标结果

  • Ans=Max{F[N,a,b]}

中间进行过一次错误的优化, 认为如果, 比如
画个图..

    HHPH
    PPPP
    PPPP
    HHPH  (红色代表有炮)

1001比1101少了一个1, 那么1101得出的结果至少不会比1001差, 可以剪掉1001. 比如上图的第三行, 没有炮的情况就可以省去,.我当时的想法是如果省掉第三行的炮, 放在第四行唯一的P上(其他情况也是相似),  第四行以下面临的情况就会比之前更加严峻, 从而不会得出更好的结果, 顶多一样.

但是我并没有考虑第三行能对第一行也产生影响 (造成这个错误的原因是状态压缩DP只记录近两行. 虽然没有明面上写, 但是情况已经记录在得分中了). 第三行的P不放炮, 则第一行和第四行都能放, 结果就更好了.

遗留问题, 如何才能在类的定义中实现二维数组的动态分配?

我写成这样

public:
    int *(a[60]);
    State (int m=0) {
        M=m;
        for(int i=0;i<stackN;i++) {
            a[i]=new int[stackN];
            memset(a[i],0xff,sizeof(int)*stackN);
        }
    }

能够compile, 在另开的程序里也没有问题, 但是在题目中答案就不一样, 我跟踪了, 在 s[i].dp(s[i-1]) 的过程中 s[i-1].a[][] 会莫名发生改变, 实际上又没有对它进行任何操作. 值得一提的是release和debug得出的答案不一样, 根据经验应该是内存的什么问题.. 也有可能是类里动态分配内存就不对头, 因为在类中定义变量不能用new..  开学去问问看

Code: (其实是我第一次用类.. 这个问题有点绕, 于是尝试封装=D )

#include <stdio.h>
#include <string.h>

int stack[]={   0,1,2,4,8,9,16,17,18,32,33,34,36,64,65,66,68,
                72,73,128,129,130,132,136,137,144,145,146,256,
                257,258,260,264,265,272,273,274,288,289,290,292,
                512,513,514,516,520,521,528,529,530,544,545,546,
                548,576,577,578,580,584,585,2E9    };       //All legal possibilities of assembles, denary.
int stackT[]={  0,1,1,1,1,2,1,2,2,1,2,2,2,1,2,2,2,2,3,1,
                2,2,2,2,3,2,3,3,1,2,2,2,2,3,2,3,3,2,3,3,
                3,1,2,2,2,2,3,2,3,3,2,3,3,3,2,3,3,3,3,4     };  //How many 1 are there in stack.
int stackN;

class State {
private:
    int M;
    bool map[10];
    bool ok(int foo);
public:
    int a[60][60];
    State (int m=0) {
        M=m;
        memset(a,0xff,sizeof(a));
    }
    void dp(State &last);
    void readMap();
};

bool State::ok(int foo) {
    for(int i=0;i<M;i++) {
        if( ((foo>>i)&1) && map[i])
            return false;
    }
    return true;
}

void State::readMap() {
    char t;
    for(int j=0;j<M;j++) {
        while(1) {
            t=getchar();
            if(t=='H' || t=='P')
                break;
        }
        map[j]= t=='H'?1:0;
    }
    return;
}

void State::dp(State &last) {
    int temp;
    for(int i=stackN-1;i>=0;i--) {
        for(int j=0;j<stackN;j++) {
            //if(a[i][j]==-2)
                //continue;
            for(int k=0;k<stackN;k++) {
                if( (stack[j]|stack[k]) & stack[i] )        //Whether two states engage.
                    continue;
                if(!ok(stack[i]))
                    continue;
                if(last.a[j][k]<0 )    //Doesn't exist.
                    continue;
                temp=last.a[j][k]+stackT[i];
                if(a[i][j]<temp)
                    a[i][j]=temp;

                /*  for(int h=0;h<i;h++)
                    if( (stack[h]|stack[i])==stack[i] )
                        a[h][j]=-2; */      //WRONG!
            }
        }
    }
    return;
}

int main() {
    int M,N;
    scanf("%d%d",&N,&M);
    if(N<0 || M<0) {
        printf("0\n");
        return 0;
    }

    for(stackN=0;stack[stackN]<(1<<M);stackN++);        //Set edge of stack according to the width M.

    State templt(M);
    State *s=new State[N+1];
    for(int i=1;i<=N;i++)
        s[i]=templt;
    //State s[101]=State(M);
    s[0].a[0][0]=0;     //Initialize.

    while(getchar()!='\n');
    for(int i=1;i<=N;i++)
        s[i].readMap();

    for(int i=1;i<=N;i++)
        s[i].dp(s[i-1]);

    int max=0;
    for(int i=0;i<stackN;i++)
        for(int j=0;j<stackN;j++)
            if(max<s[N].a[i][j])
                max=s[N].a[i][j];

    printf("%d\n",max);

    delete []s;
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *