CF1280D Miss Punyverse
题意:
$n,m<=3000$
题解:
由于 $n,m$ 数据范围不大,所以想到 $O(nm)$ 的$dp$。令 $f[i][j]$ 表示以第 $i$ 个点为根,目前划分了 $j$ 个连通块的最优方案的答案。然后考虑转移,发现没法转移,因为我们不知道根的连通块数值大小。
怎么办?总不能再记一维吧,当然不用,我们只需要考虑在答案最优的情况下,根节点所在的连通块的数值和最大的情况即可。因为如果答案不优,那么根节点无论多大,最多也就多 $1$ 的贡献,所以选取答案最优的贪心是对的,在此情况下再使根节点所在的连通块数值和最大。
重新定义一下 $dp$ 状态。令 $f[i][j]$ 表示以第 $i$ 个点为根,目前划分了 $j$ 个连通块的最优方案的答案。注意,$j$ 个连通块包括根,但是最优方案的答案不包括根。再记录一个 $Max[i][j]$ 表示在 $f[i][j]$ 尽量大的情况下,根节点所在的连通块的尽可能大的数值。转移的时候,注意分类判断当前子节点是否要合并到根节点上来,根据情况讨论。当然,这种类似树上的背包的问题都少不了一个优化,就是枚举到子节点大小。
#include<cstdio> #include<vector> #include<algorithm> #include<cstdlib> using namespace std; const long long INF=1e18; int T,n,m,a[3002],s[3002],zjds[3002]; long long Max[3002][3002],f[3002][3002]; vector<int>g[3002]; void dfs(int x,int y){ zjds[x]=1; f[x][1]=0;Max[x][1]=0; long long gg[3002],gmax[3002]; for (int i=0;i<g[x].size();i++) if (g[x][i]!=y) { dfs(g[x][i],x); for (int j=1;j<=zjds[x];j++) { gg[j]=f[x][j];gmax[j]=Max[x][j]; f[x][j]=-INF;Max[x][j]=-INF; } for (int j=zjds[x];j>=1;j--) { for (int k=1;k<=zjds[g[x][i]];k++) { if (j+k-1<=m && gg[j]+f[g[x][i]][k]>f[x][j+k-1]) { f[x][j+k-1]=gg[j]+f[g[x][i]][k];Max[x][j+k-1]=gmax[j]+Max[g[x][i]][k]; } else if (j+k-1<=m && gg[j]+f[g[x][i]][k]==f[x][j+k-1] && gmax[j]+Max[g[x][i]][k]>Max[x][j+k-1]) { f[x][j+k-1]=gg[j]+f[g[x][i]][k];Max[x][j+k-1]=gmax[j]+Max[g[x][i]][k]; } if (j+k<=m && gg[j]+f[g[x][i]][k]+(bool)(Max[g[x][i]][k]>0)>f[x][j+k]) { f[x][j+k]=gg[j]+f[g[x][i]][k]+(bool)(Max[g[x][i]][k]>0);Max[x][j+k]=gmax[j]; } else if (j+k<=m && gg[j]+f[g[x][i]][k]+(bool)(Max[g[x][i]][k]>0)==f[x][j+k] && gmax[j]>Max[x][j+k]) { f[x][j+k]=gg[j]+f[g[x][i]][k]+(bool)(Max[g[x][i]][k]>0);Max[x][j+k]=gmax[j]; } } } zjds[x]+=zjds[g[x][i]]; } for (int i=1;i<=zjds[x];i++)Max[x][i]+=s[x]; } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++)scanf("%d",&a[i]); for (int i=1;i<=n;i++) { scanf("%d",&s[i]);s[i]-=a[i]; } for (int i=1;i<=n;i++)g[i].clear(); for (int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v);g[v].push_back(u); } for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) {f[i][j]=-INF;Max[i][j]=-INF;} dfs(1,0); printf("%lld\n",f[1][m]+(bool)(Max[1][m]>0)); } return 0; }