方块堆塔(记忆化搜索)

Description
有n种立方体,每种数量不限,给出三条棱长,求最高叠多高.

堆叠条件:下方的底面长宽一定大于上方的.

Input
第一行给出 1 ≤ n ≤ 100,接下来n行每行三个数a b c 表示棱长,其中 1 ≤ a, b, c ≤ 105.

Output
最高堆叠的高度.

Sample Input
4
6 9 6
10 6 2
9 6 4
7 3 4
1
1 3 9
Sample Output
22
10

该题属于动态规划练习题

首先先理解清楚题意,给定n个立方体,求最高叠多高,这里主要注意的是,每一个立方体的堆叠方式都有3种(分别以三条棱长作为高)

各个立方体 “能否被摞”关系是一个典型的二元关系,二元关系可以用图来建模,如果b能摞在a上,a到b就有一条有边向,所以这就是一个DAG,所要求的便是DAG上的最长路径

		for(int i=1;i<=n;i++){
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);//每一种立方体都有以每条棱长为高的三种情况
			input[j].x=y;input[j].y=z;input[j++].z=x;//第一条棱长为高
			input[j].x=z;input[j].y=x;input[j++].z=y;//以第二条棱长为高 
			input[j].x=x;input[j].y=y;input[j++].z=z; //第三条棱长为高 
		}

将每种情况存储起来后,我们按照上面所说的二元关系建图即可

    bool check(int i,int j){
	if(input[i].x>input[j].x&&input[i].y>input[j].y){//两种情况,因为长和宽可以交换位置 
		return true;
	}else if(input[i].x>input[j].y&&input[i].y>input[j].x){
		return true;
	}
	return false;
    }
    m=3*n;		
    for(int i=1;i<=m;i++){
			for(int j=1;j<=m;j++){
				if(check(i,j)){//如果下标为j的立方体能放在下标为i的立方体上 
					group[i][j]=1;
				}
			}
		}

然后对该图,我们利用记忆化搜索求出最长路径即可

定义dp[i]为以下标为i的立方体为底最高堆叠高度是多少

然后对每个立方体而言,枚举每个立方体,如果能够叠上某个立方体,就叠上同时用dp数组记录子问题即可

int dfs(int i){//记忆化搜索 
	if(dp[i]!=-1){
		return dp[i];
	}
	dp[i]=input[i].z;//初始化
	for(int j=1;j<=3*n;j++){
		if(group[i][j]){//如果j立方体能放在i立方体上 
			dp[i]=max(dp[i],dfs(j)+input[i].z);
		}
	} 
	return dp[i];
}

完整代码如下:

#include
#include
#include
using namespace std;

int n;
typedef struct Node{
	int x,y,z;
}Node;
Node input[315];
int group[315][315];
int dp[315];//定义dp[i]为以i立方体为底最高堆叠高度是多少 

int dfs(int i){//记忆化搜索 
	if(dp[i]!=-1){
		return dp[i];
	}
	dp[i]=input[i].z;//初始化
	for(int j=1;j<=3*n;j++){
		if(group[i][j]){//如果j立方体能放在i立方体上 
			dp[i]=max(dp[i],dfs(j)+input[i].z);
		}
	} 
	return dp[i];
}

bool check(int i,int j){
	if(input[i].x>input[j].x&&input[i].y>input[j].y){//两种情况,因为长和宽可以交换位置 
		return true;
	}else if(input[i].x>input[j].y&&input[i].y>input[j].x){
		return true;
	}
	return false;
}

int main(){
	while((scanf("%d",&n))!=EOF){
		int j=1,m=3*n,ans=-1;
		for(int i=1;i<=n;i++){
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);//每一种立方体都有以每条棱长为高的三种情况
			input[j].x=y;input[j].y=z;input[j++].z=x;//第一条棱长为高
			input[j].x=z;input[j].y=x;input[j++].z=y;//以第二条棱长为高 
			input[j].x=x;input[j].y=y;input[j++].z=z; //第三条棱长为高 
		}
		memset(group,0,sizeof(group));//初始化 
		for(int i=1;i<=m;i++){
			for(int j=1;j<=m;j++){
				if(check(i,j)){//如果下标为j的立方体能放在下标为i的立方体上 
					group[i][j]=1;
				}
			}
		}
		memset(dp,-1,sizeof(dp));//初始化 
		for(int i=1;i<=m;i++){
			ans=max(ans,dfs(i));//得到最大情况
		}
		printf("%d\n",ans);
	}
	return 0; 
} 

你可能感兴趣的:((算法+例题)讲解,算法)