NOI 97 积木游戏

题目点我
lrj黑书上讲解动态规划的例题,通过多阶段决策思想来建立状态转移方程。
状态定义 dp(i,a,b,k) 表示当前已经有 i 堆,已经考虑过a个方块(由于考虑的顺序,最后一次考虑的方块编号为 a ),当前堆最顶上的方块编号为b,并且这个方块的摆放方式为k( k=0,1,2 ),由此状态 (i,a,b,k) 到决策结束可以达到的高度最大值为 dp(i,a,b,k)
从状态 (i,a,b,k) 开始,考虑下一个方块 a+1 时,有三种可行的决策:
1. 把方块放到当前堆上,对应的后继状态是 (i,a+1,a+1,k) a+1 按照方式 k 摆放时可以放到方块 b 按方式k摆放的平面上。状态转移方程为

dp(i,a+1,a+1,k)=dp(i,a,b,k)+height(a+1,k)

2. 这个方块另起一堆,对应后继状态 (i+1,a+1,a+1,k) ,且 i<M M 为总堆数。状态转移方程为
dp(i+1,a+1,a+1,k)=dp(i,a,b,k)+height(a+1,k)

3. 丢掉这个方块, 对应后继状态 (i,a+1,b,k) ,状态转移方程
dp(i,a+1,b,k)=dp(i,a,b,k)

再加上边界条件, dp(1,i,i,k)=height(i,k) ,即只有1块积木组成1堆的情况。这样就可以自底向上求解了。

注意枚举状态时候的边界条件。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 105

int dp[MAX][MAX][MAX][3]; // i, a, b, k

struct cube{
    int len[3];
}cubes[MAX];

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

int judge(int upper, int k1, int lower, int k2){ // k : 0, 1, 2
    if( (cubes[upper].len[(k1+1)%3] <= cubes[lower].len[(k2+1)%3] && 
         cubes[upper].len[(k1+2)%3] <= cubes[lower].len[(k2+2)%3]) ||
         (cubes[upper].len[(k1+2)%3] <= cubes[lower].len[(k2+1)%3] && 
         cubes[upper].len[(k1+1)%3] <= cubes[lower].len[(k2+2)%3])){
        return cubes[upper].len[k1];
    }
    return 0;
}

int solve(int N, int M){
    for(int a = 0; a < N-1; a++){
        for(int i = 1; i <= a+1; i++){
            for(int b = i-1; b <= a; b++){
                for(int k = 0; k < 3; k++){
                    //current pile:
                    for(int k2 = 0; k2 < 3; k2++){
                        if(0 != judge(a+1, k, b, k2))
                            dp[i][a+1][a+1][k] = max(dp[i][a+1][a+1][k], 
                                        judge(a+1, k, b, k2) + dp[i][a][b][k2]);
                    }
                    //new pile:
                    if(i < M){
                        for(int k2 = 0; k2 < 3; k2++)
                            dp[i+1][a+1][a+1][k] = max(dp[i+1][a+1][a+1][k],
                                        cubes[a+1].len[k] + dp[i][a][b][k2]);
                    }
                    //ignore:
                    dp[i][a+1][b][k] = max(dp[i][a+1][b][k], dp[i][a][b][k]);               
                }
            }
        }
    }
    int result = 0;
    for(int b = M - 1; b < N; b++)
        for(int k = 0; k < 3; k++)
            result = max(result, dp[M][N-1][b][k]);
    return result;
}

int main(){
    int N, M;

    while(EOF != scanf("%d %d", &N, &M)){
        memset(dp, 0, sizeof(int) * 3 * MAX * MAX * MAX);
        for(int i = 0; i < N; i++){
            scanf("%d %d %d", &cubes[i].len[0], &cubes[i].len[1], &cubes[i].len[2]);
            for(int k = 0; k < 3; k++)
                dp[1][i][i][k] = cubes[i].len[k];
        }
        int result = solve(N, M);
        printf("%d\n", result);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值