poj-1185

22 篇文章 0 订阅
//208K  375MS   C++
#include <cstdio>
#include <cstring>

using namespace std;

long long maxNum;

int W;
int H;
#define NN (101)
#define S (1<<9 + 1)

int degree[13] = {0, 1, 2, 4 ,8,
                    16, 32, 64, 128,
                    256, 512, 1024, 2048};

// int DP[NN][S][S];
int DP[65][65];
int temp[65][65];

int rowNumber[NN];

char mapRow[11];

int OneNum[S];

int validNum;
int valid[S];

//状态s[x]下有多少个1   
int getsum(int x)  
{   
    int num=0;
    while(x>0)
    {   
        if(x&1)num++;
        x>>=1;
    }   
    return num;
}

//状态s[x]是否造成行冲突  
bool ok(int x)   
{   
    if(x&(x<<1))return false;  
    if(x&(x<<2))return false;   
    return true;  
}

void find() {
    validNum = 0;
    memset(valid, 0, sizeof(valid));
    for(int i = 0; i < (1<<W); i++) //i枚举所有m位的二进制数  
    {
        if(ok(i)) {
            valid[validNum] = i;
            OneNum[validNum++] = getsum(i);
        }
    }
}

void beginDP() {

    for (int i = 0; i < validNum; i++) {
        if (!((~rowNumber[1]) & valid[i])) {
            DP[i][0] = OneNum[i];
        }
    }

    for (int row = 2; row <= H; row++) {
        for (int i = 0; i < validNum; i++) {
            if ((~rowNumber[row]) & valid[i]) {
                continue;
            }

            for (int i1 = 0; i1 < validNum; i1++) {
                if ((valid[i] & valid[i1])) {
                    continue;
                }
                if (row == 2 &&
                    !((~rowNumber[row-1]) & valid[i1]) &&
                    (valid[i] & valid[i1]) == 0) {

                    temp[i][i1] = OneNum[i] + OneNum[i1];
                } else if ((valid[i] & valid[i1]) == 0) {

                    int max = -1;
                    for (int k = 0; k < validNum; k++) {
                        
                        if ((valid[i] & valid[k])) {
                            continue;
                        }

                        if ((valid[i1] & valid[k])) {
                            continue;
                        }

                        if (DP[i1][k] == -1) {
                            continue;
                        }
                        int curNum = DP[i1][k] + OneNum[i];
                        max = max > curNum ? max : curNum;
                    }

                    temp[i][i1] = max;
                }
            }
        }
        memcpy(DP, temp, sizeof(DP));
    }

    maxNum = 0;
    for (int i = 0; i < validNum; i++) {
        for (int j = 0; j < validNum; j++) {
            maxNum = maxNum > DP[i][j] ? maxNum: DP[i][j];
        }
    }
}

void getSolutionNum() {
    find();
    beginDP();
    printf("%lld\n", maxNum);
}

int main() {

    while( scanf("%d %d", &H, &W) != EOF) {
        memset(DP, -1, sizeof(DP));
        memset(rowNumber, 0, sizeof(rowNumber));
        memset(temp, -1, sizeof(temp));
        for (int y = 1; y <= H; y++) {
            int x = 1;
            scanf("%s", mapRow);
            for (; x <= W; x++) {
                rowNumber[y] += (mapRow[x-1] == 'P')*degree[x];
            }
        }
        getSolutionNum();
    }
}

应该算是DP的经典中高阶题了, 妈的,把我做成跪斗士了,尤其最后的几次TLE简直令人无语。。。

做这道题之前, 刚把3254做了,心想着趁着余温把这道也给干了,谁料最后也是被反干。

一开始,还以为是3254的简单变形,只不过影响范围从一行变成了两行,不过自己也知道不会这么简单,果然原来的思路是不行的,

后来查了下,才醒悟可以将DP数组的维度扩展,以前的DP[i][j]表示 第i行的状态是 j时的最大炮兵数量,现在把DP数组扩一个维度,

DP[i][j][k]表示第i行状态是j, 第i-1行状态是k时,能部署的最大炮兵数.

那么对于DP[i][j][k]来说,其递推公式就是:

DP[i][j][k] = max(DP[i-1][k][s](s代表 i - 2行的所有可能取值) + oneNum[i]);

后面因为DP数组太大,MLE了,不得不搞了滚动数组(搞了一个temp数组来做临时数组,每一行处理完以后,再将temp数组copy到DP)。

然后又TLE了,发现是因为我在DP遍历某一行的状态i时,直接是从0 到 1<<L - 1, 其实里面有很多状态完全可以连for循环都不进,

因此可以在之前,先把所有不满足 (i&(i<<1)) 和 (i&(i<<2)) 的状态i全部拿掉,然后专门搞一个数组vaild保存满足的状态,这样,在DP的时候,只遍历valid数组即可,

改完还是TLE,跪了多次,找不到原因,后来发现自己的DP数组开的太大,改成了某个解答中的65, 立刻AC了。

无语了.. memcpy()拷贝不同尺寸数组竟然费时相差那么大? 以后要专门试一试.

先把简略报告放这里,以后还要写篇详细的.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值