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;
}
做水题一时爽,一直做水题一直爽= =