https://vjudge.net/problem/UVA-437
题目
有 $n$ 个立方体,每种都有无穷多个。要求选一些立方体摞成一根尽量高的柱子(可以自行选择哪一条边作为高),使得每个立方体的底面长款分别严格小于它下方立方体的长宽。输出最高高度。
题解
斗智题做了很久,最坑的是每次以为是和其他人AC的代码等价,结果还是WA了,甚至改了一整天还是WA,于是跳过第八章,开始做DP……
有向无环图的动态规划……
边权为高度,节点为地面正方形
因为可以旋转,所以要考虑每个立方体的个数,但是因为是无穷多个,所以就不管了= =
可以用集合栈计算机的编号的方法代替节点,事先建图(邻接矩阵,由于缓存作用,速度近似于前向星)
因为每次状态转移(把方块放上另外一个方块)不是独立的(结果会累加),因此不能用刷表法(“我为人人”),只能用填表法(“人人为我”)
因为顺序不确定,所以只能用记忆化搜索。如果要用递推,就必须将节点排序
因为状态有$mathcal{O}(3n)$个,转移有$mathcal{O}(3n)$种,时间复杂度为$mathcal{O}(n^2)$
AC代码
#include<bits/stdc++.h> using namespace std; #define REP(r,x,y) for(register int r=(x); r<(y); r++) #define PER(r,x,y) for(register int r=(x); r>(y); r--) #define REPE(r,x,y) for(register int r=(x); r<=(y); r++) #define PERE(r,x,y) for(register int r=(x); r>=(y); r--) #ifdef sahdsg #define DBG(...) printf(__VA_ARGS__) #else #define DBG(...) (void)0 #endif #define MAXN 37 #define MAXID 300 struct npii { int a,b;//a>=b npii(){} npii(int x, int y) { if(x>y) a=x,b=y; else a=y,b=x; } bool operator<(const npii&rhs) const { return a<rhs.a || (a==rhs.a && b<rhs.b); } }; int n; int arr[MAXN][3]; map<npii,int> mp; npii st[MAXID]; int h[MAXID]; int dp[MAXID]; int id=0; inline bool vali(int i, int j) { return st[i].a<st[j].a && st[i].b<st[j].b; } bool E[MAXID][MAXID]; inline int solve(int x) { if(dp[x]>=0) return dp[x]; int ans=h[x]; REP(i,0,id) { if(E[x][i]) { ans = max(ans, solve(i)+h[x]); } } return dp[x]=ans; } int main() { int kase=0; while(~scanf("%d", &n) && n) { kase++; id=0; mp.clear(); memset(dp,-1,sizeof dp); REP(i,0,n) { scanf("%d%d%d", &arr[i][0], &arr[i][1], &arr[i][2]); #define OP(x,y,z) if(mp.count(npii(arr[i][x],arr[i][y]))==0) {mp[npii(arr[i][x],arr[i][y])]=id;st[id]=npii(arr[i][x],arr[i][y]);h[id]=arr[i][z];id++;} OP(0,1,2); OP(1,2,0); OP(0,2,1); #undef OP } memset(E,0,sizeof E); REP(i,0,id) { REP(j,0,id) { if(vali(i,j)) { E[i][j]=1; } } } int maxx=0; REP(i,0,id) { maxx = max(maxx,solve(i)); } printf("Case %d: maximum height = %d ", kase, maxx); } return 0; }
做水题一时爽,一直做水题一直爽= =