题目点我
lrj黑书上讲解动态规划的例题,通过多阶段决策思想来建立状态转移方程。
状态定义
dp(i,a,b,k)
表示当前已经有
i
堆,已经考虑过
从状态
(i,a,b,k)
开始,考虑下一个方块
a+1
时,有三种可行的决策:
1. 把方块放到当前堆上,对应的后继状态是
(i,a+1,a+1,k′)
且
a+1
按照方式
k′
摆放时可以放到方块
b
按方式
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 为总堆数。状态转移方程为
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;
}