不相邻问题——状压Dp——HDU1565,POJ3254

先做的HDU1565方格取数问题,在格点取数,所取得数不能相邻,问最大能取得总和是多少

容易想到——dp[i][j]表示当前i行j状态所取得数,如何dp呢???
首先针对每一行找到可以通过得方方案(左右不相邻即可——存储到pass_state【】中)

void find_pass()
{
    for(int i = 0;i < (1 << N);i++)
    {
        if((i & (i << 1)))continue;
        pass_state[++tot] = i;
    }
}
然后针对每一行得每一个可行的状态,要求出其这个状态对应的值得和,方便后续的状态转移求取最大值

int array_pass[25][18000];
int get_part_sum(int x,int h)
{
    int sum = 0;
    for(int i = 0;i < N;i++)
    {
        if((x & (1 << i)) > 0)
        {
            sum += cost[h][i];
        }
    }
    return sum;
}
void save_pass_state()
{
    for(int i = 1;i <= tot;i++)
    {
        array_pass[0][i] = dp[0][i] = get_part_sum(pass_state[i],0);
        for(int j = 1;j < N;j++)
        {
            array_pass[j][i] = get_part_sum(pass_state[i],j);
        }
    }
}

一开始顺便初始化了第一行的dp值,因为第一行特殊,不需要考虑上下相邻得情况
这样我们就可以从第二行开始比——外层一个行循环,往里是当前状态,最里面是上一行的状态,如果满足上下不相邻得情况那就可以尝试状态转移了

void find_return()
{
    for(int i = 1;i < N;i++)
    {
        for(int j = 1;j <= tot;j++)
        {
            for(int k = 1;k <= tot;k++)
            {
                if((pass_state[j] & pass_state[k]) > 0)continue;
                dp[i][j] = max(dp[i][j],dp[i - 1][k] + array_pass[i][j]);
            }
        }
    }
}
void get_ret()
{
    int ret_max = -1;
    for(int i = 1;i <= tot;i++)
    {
        ret_max = max(ret_max,dp[N - 1][i]);
    }
    printf("%d\n",ret_max);
}


最后的输出是在最后一行的所有可行状态中选出一个最大得输出……

然后看一下Poj3254,其实还真的差不多,只不过这个想让求总的方法数,而且对于可行状态也有了自己的定义不仅仅是不相邻了,其实无非就是在原先的暴力中添加一次筛选,只有满足输入状体的才可以进行下去

依旧是先去寻找可行的不相邻(左右)得状台

void get_ret()
{
    int ret_max = -1;
    for(int i = 1;i <= tot;i++)
    {
        ret_max = max(ret_max,dp[N - 1][i]);
    }
    printf("%d\n",ret_max);
}
然后有了两个判断——一个是判断当前行得状态满不满足输入得状态限制——一个是判断上下想不相邻

bool is_pass_mp_state(int state,int line)
{
    state = pass_state[state];
    for(int i = 0;i < M;i++)
    {
        if(((mp[line] >> i) & 1) == 0 && ((state >> i) & 1) == 1)
            return false;
    }
    return true;
}
bool is_pass_state_line_by_line(int j,int k)
{
    j = pass_state[j];
    k = pass_state[k];
    for(int i = 0;i < M;i++)
    {
        if(((j >> i) & 1) == 1 && ((k >> i) & 1 ) == 1)
            return false;
    }
    return true;
}

这样作为dp得基础初始化第一行得值~,并进行层层更新

void init_first_state()
{
    for(int i = 1;i <= tot;i++)
    {
        if(is_pass_mp_state(i,0))
        {
            dp[0][i] = 1;
        }
    }
}

void update_layer_upon_layer()
{
    for(int i = 1;i < N;i++)
    {
        for(int j = 1;j <= tot;j++)
        {
            for(int k = 1;k <= tot;k++)
            {
                if(is_pass_state_line_by_line(j,k) && is_pass_mp_state(j,i))
                {
                    dp[i][j] += dp[i - 1][k];
                }
            }
        }
    }
}
最后的方法总数也就得到了

void get_sum()
{
    int sum = 0;
    for(int i = 1;i <= tot;i++)
    {
        //cout<
有一个地方没有提到就是对于地图状态得转化0  1 输入还好做,熟练以下位运算就好了

for(int i = 0;i < N;i++)
        {
            for(int j = M - 1;j >= 0;j--)
            {
                scanf("%d",&num);
                if(num)mp[i] += (1 << j);
            }
        }

你可能感兴趣的:(算法入门)