DP:炮兵阵地问题(POJ 1185)

              正确的打炮方式(大雾)(点我查看)

  2015-08-21

  问题是中文的,大家可以进去看看。

  先说一个坑,这个问题我交了很多次,都没过,反正是WA到我烦了,都不知道哪里错了!!!怎么会有错,然后翻了一下别人的代码,立马懂了

  原因就是这题是因为他输入是字符,我一般清字符就直接用一个fflush(stdin)就完事了,上次石子游戏就坑了我一次,所以我这次没用fflush了,因为fflush会清掉整个缓冲区,而在线提交的原理就是把一大堆测试数据放缓冲区一个一个读的,真是太坑了,我以为在线编译器会直接忽略\n的,然而显然它没有

                      

  OK我们回到这一题上来,这一题和我上一次写过的稻田养牛(点我查看)比较像,那么区别在哪里呢?嗯,区别就在这一题要存两个位置,因为炮兵可以打到两个区域,而且左右的判断规则也不同,并且这一题是要算的最大数量(而不是组合数)。嗯。

  那么我们现在要想的问题就是怎么保存两个位置就好了,并且这两行的信息必须集中到一起(不然怎么叫状态压缩呢),所以我们立马就想到了用一个二维数组。同时,因为这题是算的最大数量,我们可以规定一个“不合法位置”为-1,来简化判断条件(这样就不用再和图的上一行和上上一行的P,H位置进行比较了,比较方便)。

  那么,最后我们得出的二维数组将会蕴含下列信息:

     1.至上一行为止的每一个状态所对应的最大炮兵数

     2.并且上一行与上上一行肯定满足所有的对应限制(不然肯定会显示-1非合法位置)

    所以,我们现在只用本行和上上一行的炮兵满足不在一列的要求就可以了,也是蛮方便的。

    状态转移方程:Now_State[j][k]=max(Now[j][k],Prev[k][h]+State_men[j]);很简单

这个题的确比稻田那个要难一点,主要是两行情况要注意一下,而且还要算炮兵数(这个有一个技巧,在注释写了)

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

void Search(const int, const int, int *const);
void Inivilize_Valid_State(const int, int *const, int **, int **);
void ReMark(int **,const int);
int **Inivilize_Sum_State(const int);
static int If_Valid(const int, const int);
static int Cal_Men_Of_State(const int);
static int max_(const int, const int);

int main(void)
{
    int M, N, i, j, *line = NULL;
    char tmp;

    while (scanf("%d%d", &N, &M) != EOF)
    {
        //H的比特位就是1,P就是1
        line = (int *)malloc(sizeof(int)*N);
        for (i = 0; i < N; i++)
        {
            getchar();
            line[i]= 0;
            for (j = 0; j < M; j++)
            {
                tmp = getchar();
                if (tmp == ‘H‘)
                    line[i] |= (1 << (M - j - 1));
            }
        }
        Search(N, M, line);
        free(line);
    }
    return 0;;
}

static int If_Valid(const int state1, const int state2)
{
    /*Name:            If_Valid
     *Function:        判断相邻两行之间是否违反了炮兵不能在同一列的规定(或者隔行)
                    判断是否违反了H位置不能放炮兵的规定
     *Return:        合法返回1,不合法返回0
    */
    if (state1&state2)
        return 0;
    return 1;
}

void Inivilize_Valid_State(const int n, int *const valid_state_sum, int **Valid_State, int **State_men)
{
    /*Name:            Inilivilize:
     *Function:        遍历每一行可能的组合方式,找到可以放炮兵的那些合法位置,存起来
     *return:        无
    */
    *valid_state_sum = 0;
    *Valid_State = (int *)malloc(sizeof(int)*n);
    *State_men = (int *)malloc(sizeof(int)*n);
    int i;
    for (i = 0; i < n; i++)
    {
        if ((!(i&(i << 1))) && (!(i&(i << 2)))){
            //这个表示当一个位置是1时,其左右的两个位置都必须是空位置才行
            //比如100100这个就是合法位置,而110100这些就不行
            (*Valid_State)[(*valid_state_sum)] = i;
            (*State_men)[(*valid_state_sum)++] = Cal_Men_Of_State(i);
        }
    }
}

static int Cal_Men_Of_State(const int i)
{
    /*Name:            Cal_Men_Of_State
     *Function:        计算每一个合法位置的1的个数(也就是放炮兵的个数)
     *返回值:        这个合法位置的炮兵的个数
    */
    int k = i, sum_men = 0;
    while (k)
    {
        //这个位运算的意思是去掉所有的1,然后统计去掉的1的数量
        //比如1010,-1以后变成1001,然后再和1010按位与,就得出了1000,去了一个1
        //1000再-1变0111,按位与变0000,又去掉一个1,然后k=0,退出循环
        sum_men++;
        k &= (k - 1);
    }
    return sum_men;
}

inline int **Inivilize_Sum_State(const int valid_state_sum)
{
    /*Name:            Inivilize_Sum_State
     *Function:        初始化Now Prev位置
     *Return:        返回对应地址
    */
    int **tmp = (int **)malloc(sizeof(int)*valid_state_sum);
    int i;
    for (i = 0; i < valid_state_sum; i++)
    {
        tmp[i] = (int *)malloc(sizeof(int)*valid_state_sum);
        memset(tmp[i], (int)-1, sizeof(int)*valid_state_sum);
    }
    return tmp;
}

void ReMark(int **tmp, const int valid_state_sum)
{
    /*Name:            ReMark
    *Function:        初始化Now位置
    *Return:        返回对应地址
    */
    int i;
    for (i = 0; i < valid_state_sum; i++)
        memset(tmp[i], (int)-1, sizeof(int)*valid_state_sum);
}

static int max_(const int a, const int b)
{
    return a > b ? a : b;
}

void Search(const int line, const int col, int *const Gragh)
{
    /*Name:            Search
     *Function:        得出最大炮兵数量
     *Return:        无
    */
    int valid_state_sum, i, j, k, h, max;
    int *State_men = NULL, *Valid_State = NULL;
    Inivilize_Valid_State((1 << col), &valid_state_sum, &Valid_State, &State_men);
        //注意(1<<col)是表示有多少列就有2^col个比特位的组合哦

    int **Now_State = Inivilize_Sum_State(valid_state_sum);
    int **Prev = Inivilize_Sum_State(valid_state_sum);
    int **ec = NULL;

    for (i = 0; i < valid_state_sum; i++)
    {
        if (If_Valid(Gragh[0], Valid_State[i]))
            Now_State[i][0] = State_men[i];
    }
    ec = Prev; Prev = Now_State; Now_State = ec;
    ReMark(Now_State, valid_state_sum);

    for (i = 1; i < line; i++)
    {
        for (j = 0; j < valid_state_sum; j++)
        {
            if (If_Valid(Gragh[i], Valid_State[j]))
            {
                for (k = 0; k < valid_state_sum; k++)
                {
                    if (!(Valid_State[k] & Valid_State[j]))
                    {
                        for (h = 0; h < valid_state_sum; h++)
                        {
                            if (!(Valid_State[h] & Valid_State[j])
                                && Prev[k][h] != -1)
                                Now_State[j][k] = max_(Now_State[j][k], Prev[k][h] + State_men[j]);
                        }
                    }
                }
            }
        }
        ec = Prev; Prev = Now_State; Now_State = ec;
        ReMark(Now_State, valid_state_sum);
    }
    for (i = 0, max = 0; i < valid_state_sum; i++)
        for (j = 0; j < valid_state_sum; j++)
            max = max_(Prev[i][j], max);

    printf("%d\n", max);
    for (i = 0; i < valid_state_sum; i++)
        free(Now_State[i]);
    for (j = 0; j < valid_state_sum; j++)
        free(Prev[j]);
    free(Now_State); free(Prev);
}

最后说一下,如果这道题要你算组合数,那么就不能标记-1为不合理位置了,那必须上一行和上上一行要和PH位置进行两次对比,然后还要上一行和上上一行不产生冲突才行

另外这题WA真是太恶心了,搞得我最近几天都不想写代码了,真是醉

  

时间: 2024-11-05 19:42:08

DP:炮兵阵地问题(POJ 1185)的相关文章

炮兵阵地(POJ 1185状压dp)

题意:n*m地图'H'能放'p'不能放,布兵的方格上下左右不能布兵,给你地图求最大布兵数 分析:关系到前两行,所以dp[i][j][k]第i行状态为j,i-1行状态为k时的最大布兵数, 先求出所有可行的状态,统计出其布兵数. #include <map> #include <set> #include <list> #include <cmath> #include <queue> #include <stack> #include

状态压缩dp 炮兵阵地

题面: 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用”H” 表示),也可能是平原(用”P”表示),如下图. 在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格. 图上其它白色网格均攻击不到. 从图上可见炮兵的攻击范围不受地形的影

POJ 1185 炮兵阵地 状压dp

http://poj.org/problem?id=1185 经典题目不必多说,直接贴代码. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int n, m, cnt, size; 7 int a[110], st[70], ct[70]; 8 char str[15]; 9 int f[110][70][70]; 10 void init(

POJ 1185 炮兵阵地 状压DP+离散化优化

一开始能想到的状态就只有位压两行和当前行的行号,这样无论是空间和时间都是无法接受的. 但是因为炮兵的攻击范围比较大,而且又有地形限制,每一行的状态其实不多了,打表看了一下不超过80种,离散化一下就可以随意DP了. 据说题目也可以抽象成二分图最大匹配来搞?感觉复杂度有点高 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #i

POJ 1185 炮兵阵地 (状压dp 经典中的经典)

炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 21381   Accepted: 8290 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击

POJ 1185炮兵阵地 (状压DP)

题目链接 POJ 1185 今天艾教留了一大堆线段树,表示做不动了,就补补前面的题.QAQ 这个题,我第一次写还是像前面HDU 2167那样写,发现这次影响第 i 行的还用i-2行那样,那以前的方法就行不通了. 找出所有可行的状态,因为每一行最大只有10列,所以一行里最多有4个,那它可行的状态不多(网上大多数说法最多是60个).用dp[x][i][j]来转移,x表示第x行,i表示第x行的状态,j表示第x-1行的状态.先初始化前两行. 1 #include <cstdio> 2 #include

Poj 1185 炮兵阵地(状态压缩dp 入门题)

炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 17272   Accepted: 6593 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击

poj 1185 炮兵阵地 (状态压缩DP)

炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 19690   Accepted: 7602 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击

poj - 1185 炮兵阵地 状压DP 解题报告

炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 21553   Accepted: 8363 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示).也可能是平原(用"P"表示),例如以下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不可以部署炮兵部队).一支炮兵部队在地图上的