1. 程式人生 > 實用技巧 >UVA437 【巴比倫塔 The Tower of Babylon】

UVA437 【巴比倫塔 The Tower of Babylon】

一道DAGdp

思路

首先要明確,我們要求的是最長路,圖可以根據立方體間的關係來建,如果這個立方體可以放在另一個上面,就從代表這個立方體的點向那個點連一條有向邊。
因為兩條邊都是嚴格小於下面的立方體的,所以這個圖一定是個有向無環圖(DAG)

另外,由於每個立方體有三種高,所以總共會有3n種不同的立方體,空間要開三倍。

這裡我是用的鄰接矩陣建圖+記憶化搜尋;

程式碼如下(附註釋)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,d[100][3],kase,sh[31][3];//d是記憶化用的陣列 
int tot;
bool G[100][100];//因為只要判斷能不能搭上去,所以只用bool,節省空間 
struct node{
	int x,y,su,h;//x,y,h分別代表這個立方體的長寬高,su代表高的型別 
}S[100];
int dp(int num,int su)//num是當前立方體的編號,su意思同上 
{
	int& ans=d[num][su];
	if(ans>0)return ans;//記憶化 
	ans=S[num].h;
	for(int i=1;i<=tot;i++)
	if(G[num][i])ans=max(ans,dp(i,S[i].su)+S[num].h);//如果有連邊,就更新
	return ans;
}
int main()
{
	while(1)
	{
		scanf("%d",&n);
		if(!n)break;	
		memset(d,0,sizeof(d));
		memset(G,0,sizeof(G));//多組資料記得初始化 
		int maxn=-1;tot=0;
		for(int i=1;i<=n;i++)scanf("%d%d%d",&sh[i][0],&sh[i][1],&sh[i][2]);//讀入三種邊 
		printf("Case %d: maximum height = ",++kase);
		for(int i=1;i<=n;i++)
		{
			for(int j=0;j<3;j++)//記錄每一種型別的立方體 
			{
				S[++tot].su=j;
				S[tot].h=sh[i][j];
				if(j==0)
				S[tot].x=sh[i][1],S[tot].y=sh[i][2];
				if(j==1)
				S[tot].x=sh[i][0],S[tot].y=sh[i][2];
				if(j==2)
				S[tot].x=sh[i][0],S[tot].y=sh[i][1];
			}
		}
		for(int i=1;i<=tot;i++)//建圖 
		{
			for(int j=1;j<=tot;j++)
			{
				if(i!=j&&((S[i].x<S[j].x&&S[i].y<S[j].y)||(S[i].x<S[j].y&&S[i].y<S[j].x)))
				{
					G[i][j]=1;
				}
			}
		}
		for(int i=1;i<=tot;i++)
		{
			for(int j=0;j<3;j++)
			maxn=max(maxn,dp(i,j));
		}
		printf("%d\n",maxn);
	}
	return 0;
}