CF815C的空间优化

Euler_Pursuer

2018-10-12 11:27:24

Solution

题目很简单,但是想优化却不容易。 我们可以很明显的列出DP方程,也就是考虑某个优惠券是否使用来转移状态。它使用了,那么依赖它的优惠券可使用可不使用;它没使用,那么依赖它的优惠券就不能使用。参考@[feng_chengjie](https://www.luogu.org/space/show?uid=36294) 的题解,设dp[u][j][0/1]是以u为根子树中,u用或者不用优惠券时,选j件物品需要的最小代价DP方程: $$dp[u][sum][1]=\min(dp[u][sum][1],dp[u][i][1]+dp[son][sum-i][1])$$ $$dp[u][sum][1]=\min(dp[u][sum][1],dp[u][i][1]+dp[son][sum-i][0])$$ $$dp[u][sum][0]=\min(dp[u][sum][0],dp[u][i][0]+dp[son][sum-i][0])$$ 通过枚举一个节点的儿子,转移方程即可。 但是我们发现它的空间复杂度为$O(n^2)$,而且在转移的过程中,某个节点转移完毕之后其DP数组是没用的,也就是这样浪费了很多空间。所以我们可以考虑对某个儿子DP完毕之后,释放掉该儿子占用的空间,我们可以用vector实现。 考虑其空间复杂度的最坏情况,注意到我们每次都是在DP完子树之后才对该节点扩充空间,也就是说每个节点至少要有两个儿子才能对空间产生一定的“浪费”,而每次扩充的空间都是该节点前面的子树的大小,对于当前儿子来说,它扩充的空间又是它的当前考虑的儿子的左兄弟,而我们上述考虑的节点均互不相同,因此累积起来空间复杂度为$O(n)$。当然,vector可能有点小常数,但是实际测试中时间会比优化前更快。 对于vector的释放空间的操作,不是什么clear就能解决的事。因为clear看上去空间清空了,实际上指针还在,你可以试试。实质上的释放空间是用【`vector<类型>().swap(要释放空间的vector变量)`】,扩充空间用【`变量.resize(扩充到多大空间,填充内容)`】。我的程序最慢点耗时187ms,最大空间消耗为1.27MB。 ```cpp #include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> using namespace std; vector<int> G[5005]; struct F { long long y, n; }; vector<F> f[5005]; int c[5005],`d[5005], si[5005], n, q, tot; inline void add(int a, int b) { G[a].push_back(b); } void dfs(int x) { int s = G[x].size(), e; long long rr, ss, tt; si[x] = 1; //f[x].insert(f[x].begin(), n+1, (F){998244353998244353, 998244353998244353}); f[x].resize(2); f[x][0].n = 0;///什么都不用 f[x][0].y = 998244353998244353; f[x][1].y = c[x] - d[x]; f[x][1].n = c[x]; ///没有使用优惠券 ///使用了优惠券 for(register int i = 0; i < s; i += 1) { e = G[x][i]; dfs(e); f[x].resize(si[x]+si[e]+1, (F){998244353998244353, 998244353998244353}); for(register int j = si[x]; j >= 0; j -= 1) for(register int k = 0; k <= si[e]; k += 1) { rr = f[x][j].n+f[e][k].n; ss = f[x][j]`y+f[e][k].n; tt = f[x][j].y+f[e][k].y; if(rr <= tot && f[x][j+k].n > rr) f[x][j+k].n = rr; if(ss <= tot && f[x][j+k].y > ss) f[x][j+k].y = ss; if(tt <= tot && f[x][j+k].y > tt) f[x][j+k].y = tt; } vector<F>().swap(f[e]); si[x] += si[e]; } //if(!s) // f[x].resize(f[x].begin()+2, n-1, (F){998244353998244353, 998244353998244353}); } int main() { scanf("%d%d", &n, &tot); scanf("%d%d", &c[1], &d[1]); for(register int i = 2; i <= n; i += 1) { scanf("%d%d%d`", &c[i], &d[i], &q); add(q, i); } dfs(1); for(register int i = n; i; i -= 1) { printf("%lld %lld\n", f[1][i].y, f[1][i].n); if(f[1][i].y <= tot || f[1][i].n <= tot) { printf("%d", i); return 0; } } printf("0`");//might wrong at here return 0; } ``` - - - 抄题解的你绝对过不了。