http://poj.org/problem?id=3017
题意:N个结点的树,有M个叶子结点,取每条边有花费Ci,取每个叶子结点有价值Pi;
求:在保证总收入(总价值-总花费)>0的情况下,能取到的叶子结点的最大数目
Idea:其实就是把背包给扩展到了树结构,通过TreeDP+分组背包来实现。
#include <cstdio> #include <fstream> #include <iostream> #include <cstring> #include <string> using namespace std; #define maxn(x,y) ((x)>(y)?(x):(y)) const int INF_MIN = -(1<<28); struct node{ int ev,next; }; int c[3010],maxn[3010]; int N,M; int price[3010]; node a[100000]; int fa; int head[3010]; int f[3010][3010]; void Add_edge(int fv,int ev,int cost) { fa++; a[fa].ev = ev; c[ev] = cost; a[fa].next = head[fv]; head[fv] = fa; maxn[fv]++; } void TreeDp(int fv) { int i,k,j,m; if (head[fv] == -1){ f[fv][1] = price[fv] - c[fv]; return; } for (i = head[fv];i != -1;i = a[i].next) { k = a[i].ev; TreeDp(k); maxn[fv] += maxn[k]; for (j = maxn[fv];j >= 1;j--)//开始时偷懒,j的范围直接从M->1,结果TLE;后来改成了maxn[fv];其实也应该,这个循环是在递归中,以前是在main中,调用次数相差很大 { for (m = 1;m <= j;m++) { f[fv][j] = maxn(f[fv][j],f[fv][j-m]+f[k][m]); } } } for (i = 1;i <= M;i++) f[fv][i] -= c[fv];//f[fv][i] -= c[fv]; 这里也比较坑,样例都改了好久才过,思维不够严密啊 } int main() { //freopen("test.txt","r",stdin); int i,j,k,t1,t2; int K; while(~scanf("%d%d",&N,&M)) { fa = 0; memset(head,-1,sizeof(head)); memset(maxn,0,sizeof(maxn)); c[1] = 0; for (i = 1;i <= N-M;i++) { scanf("%d",&K); for (j = 1;j <= K;j++) { scanf("%d%d",&t1,&t2); Add_edge(i,t1,t2);//Add_edge(j,t1,t2); } } for (i = N-M+1;i <= N;i++)//for (i = 1;i <= M;i++) { scanf("%d",&price[i]); } for (i = 0;i <= N;i++) { f[i][0] = 0; for (j = 1;j <= M;j++) { f[i][j] = INF_MIN; } } TreeDp(1); for (i = M;i >= 0;i--) { if (f[1][i] >= 0) break; } printf("%d\n",i); } return 0; } /* code: debug:1st返回WA,估计是输出代码没注释掉;2rd返回LTE,少了个比较明显的优化(主要没想到居然会卡这个优化) */