题目大意
你有N种石头,给定石头的长宽高,每种石头有无限块,石头的叠放顺序是上一个石头的长宽比下一个石头的长宽要严格大于,要求叠起来的石头高度最大值。1<=n<=30。
思路
方法一:拓扑序+dp。动规方程dp[i]=max(dp[i],dp[j]+stone[i]),dp方向是拓扑序。这题之所以能用拓扑序+dp是因为每种石头只能放一次,虽说有无限块,但是限于叠放顺序的原则是不能多放的。而输入的石头可以有三种放法,因此石头种数时N*3,然后构建图并且以拓扑序进行dp。
方法二:DAG+DP。这种做法也是需要构建一个图(a>b则连起一条自a向b的有向边),原理好像和拓扑序一致。不过有一点是遍历方向,DAG里点是“去中心化”的,对每个点都进行dp更新。而上一种做法是按照拓扑序来进行的,是有优先级的。
代码(方法一)
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
struct Blocks{
int x,y,z;
};
int n;
vector<Blocks> box;
queue<int> q;
bool graph[105][105];
int v[105],in[105];
int main()
{
//freopen("1.txt","r",stdin);
//freopen("2.txt","w",stdout);
int x,y,z,cnt=1;
while(scanf("%d",&n)!=EOF&&n){
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x,&y,&z);
Blocks a;
a.x=x;a.y=y;a.z=z;
box.push_back(a);
a.x=y;a.y=z;a.z=x;
box.push_back(a);
a.x=z;a.y=x;a.z=y;
box.push_back(a);
}
for(int i=0;i<box.size();i++){
for(int j=0;j<box.size();j++){
if(i==j)continue;
if(box[i].x>box[j].x&&box[i].y>box[j].y){
graph[i][j]=1;
in[j]++;
}
else if(box[i].x>box[j].y&&box[i].y>box[j].x){
graph[i][j]=1;
in[j]++;
}
}
}
int ans=0;
for(int i=0;i<box.size();i++){
//printf("%d %d %d %d\n",in[i],box[i].x,box[i].y,box[i].z);
if(in[i]==0){
q.push(i);
v[i]=box[i].z;
ans=max(ans,v[i]);
}
}
//printf("\n");
while(!q.empty()){
int head=q.front();
q.pop();
//printf("%d %d %d\n",box[head].x,box[head].y,box[head].z);
//printf("in[%d]=%d %d %d %d\n",head,in[head],box[head].x,box[head].y,box[head].z);
for(int i=0;i<box.size();i++){
if(graph[head][i]){
in[i]--;
v[i]=max(v[i],v[head]+box[i].z);
ans=max(ans,v[i]);
if(in[i]==0)q.push(i);
}
}
}
printf("Case %d: maximum height = %d\n",cnt++,ans);
box.clear();
memset(graph,0,sizeof(graph));
memset(v,0,sizeof(v));
memset(in,0,sizeof(in));
}
}