POJ-1155 TELE (树形DP+分组背包)
题目大意:给一棵带边权的有根树,每个叶子节点有权。边权表示代价,叶子节点的权值代表可以补偿多少代价。问从根节点最多可以到达多少个叶子,使得付出的总代价不大于0。
题目分析:定义状态dp(u,k)表示从u开始到达k个叶子所花费的最小代价。则状态转移方程为:
dp(u,k)=min(dp(u,k),dp(son,j)+dp(u,k-j)+u到son的代价)。
ps:要加上优化,否则超时。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; const int N=3005; const int INF=1000000000; struct Edge { int to,w,nxt; }; Edge e[N]; int n,m,cnt; int w[N]; int head[N]; int dp[N][N]; void add(int u,int v,int w) { e[cnt].to=v; e[cnt].w=w; e[cnt].nxt=head[u]; head[u]=cnt++; } void init() { int k,a,b; cnt=0; memset(head,-1,sizeof(head)); for(int i=1;i<=n-m;++i){ scanf("%d",&k); while(k--) { scanf("%d%d",&a,&b); add(i,a,b); } } for(int i=n-m+1;i<=n;++i) scanf("%d",w+i-n+m-1); } int dfs(int u) { for(int i=0;i<=m;++i) dp[u][i]=INF; dp[u][0]=0; if(u>n-m){ dp[u][1]=-w[u-n+m-1]; return 1; } int tot=0; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; tot+=dfs(v); for(int j=tot;j>=1;--j) for(int k=0;k<=j;++k) dp[u][j]=min(dp[u][j],e[i].w+dp[v][k]+dp[u][j-k]); } return tot; } void solve() { dfs(1); int ans=0; for(int i=m;i>=0;--i){ if(dp[1][i]<=0){ ans=i; break; } } printf("%d\n",ans); } int main() { while(~scanf("%d%d",&n,&m)) { init(); solve(); } return 0; }