蒟蒻的树形dp记录
POJ2342:
题意:某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上司,现在已知每个人的活跃指数和上司关系(当然不可能存在环),求邀请哪些人(多少人)来能使得晚会的总活跃指数最大。
题解:
当i来的时候,dp[i][1] += dp[j][0];//j为i的下属
当i不来的时候,dp[i][0] +=max(dp[j][1],dp[j][0]);//j为i的下属
#include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <queue> #include <typeinfo> #include <map> //#include<bits/stdc++.h> typedef long long ll; using namespace std; #define inf 10000000 inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } //*************************************************************** int dp[10000][2]; int vis[10000]; int parent[10000]; int n,a,b; void sp(int x) { vis[x]=1; for(int i=1;i<=n;i++) { if(parent[i]==x&&!vis[i]) { sp(i); dp[x][0]+=max(dp[i][0],dp[i][1]); dp[x][1]+=dp[i][0]; } } } int main() { while(cin>>n) { memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) scanf("%d",&dp[i][1]); memset(vis,0,sizeof(vis)); int root=0; memset(parent,0,sizeof(parent)); while(scanf("%d%d",&a,&b)&&a&&b) { parent[a]=b; root=b; } sp(root); printf("%d\n",max(dp[root][0],dp[root][1])); } return 0; }
vijos1642
题解:我们先依次求出每棵树的先根遍历序,并保存在同一个序列list[]中。 为了利用上面的结论,我们还要求出以节点i为根的子树的节点总数count[i]。
定义: v[i]表示第i个物品的权值 dp[i][j]表示从第i个物品到第n个物品,最多花费j,能得到的最大权值和。
状态转移: 对于一个节点,我们考虑是否购买它: 购买:那么我们继续考虑它后面的节点 不购买:那么我们跳过它的子孙节点
方程如下: F[i][j]=Max{F[i+1][j-cost[list[i]]]+weight[list[i]],F[i+count[list[i]]][j]}
///1085422276 #include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <queue> #include <typeinfo> #include <map> typedef long long ll; using namespace std; #define inf 10000000 inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } //*************************************************************** ///先根遍历序 int list[10005]; int s[1005],a[1081][1008]; int v[10001],counts[1901]; int dp[1091][1991]; int q; int n,m; int dfs(int k) { list[++q]=k; if(s[k]==0){return ++counts[k];} for(int i=1;i<=s[k];i++) { counts[k]+=dfs(a[k][i]); } return ++counts[k]; } int main() { int t; memset(dp,-127,sizeof(dp)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d%d",&v[i],&t); while(t--) { scanf("%d",&a[i][++s[i]]); } } dfs(1); for(int i=1;i<=n;i++){dp[i][0]=0;dp[i][1]=v[list[i]];} for(int i=n-1;i>=0;i--) { for(int j=1;j<=m;j++) { dp[i][j]=max(dp[i+1][j-1]+v[list[i]],dp[i+counts[list[i]]][j]); } } int ans=-inf; for(int i=1;i<=m;i++) ans=max(dp[1][i],ans); printf("%d\n",max(ans,0)); return 0; }
POJ 1947
题解:dp[root][i]表示以root为根得到节点数为i 所需要删除的少边数
转移:
dp[root][i]=dp[root][i]+1; 不加某son,边+1;
dp[root][i]=min(dp[root][i-j]+dp[son][j]);加某son,j代表son为根的树节点数
最后注意:非根节点边数+1;因为要删除与父亲节点的边
///1085422276 #include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <queue> #include <typeinfo> #include <map> typedef long long ll; using namespace std; #define inf 10000000 inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } //*************************************************************** int dp[200][200]; vector<int >son[200]; int dfs(int root,int pre) { dp[root][1]=0; int sum=1; for(int i=0;i<son[root].size();i++) { int s=son[root][i]; if(s==pre)continue; sum+=dfs(s,root); for(int k=sum;k>=1;k--) { dp[root][k]=dp[root][k]+1; for(int h=1;h<k;h++) { dp[root][k]=min(dp[root][k],dp[root][k-h]+dp[s][h]); } } } return sum; } int main() { int n,m,a,b; memset(dp,127,sizeof(dp)); scanf("%d%d",&n,&m); // for(int i=1;i<=n;i++)dp[i][0]=1; for(int i=1;i<n;i++) { scanf("%d%d",&a,&b); son[a].push_back(b); son[b].push_back(a); } dfs(1,-1); int ans=dp[1][m]; for(int i=2;i<=n;i++){ if(dp[i][m]+1<ans)ans=dp[i][m]+1; } printf("%d\n",ans); return 0; }
HDU 1011 Starship Troopers
转移:dp[i][k] = max(dp[i][k], dp[i][k-l] + dp[i][l]);
//зїеп:1085422276 #include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <queue> #include <typeinfo> #include <map> #include <stack> typedef long long ll; #define inf 100000000 using namespace std; inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } //*************************************** int v[888]; int cost[888]; int dp[333][333]; vector<int >tr[333]; int n,m; void dfs(int k,int pre) { for(int i=cost[k];i<=m;i++) dp[k][i]=v[k]; for(int i=0;i<tr[k].size();i++){ if(pre==tr[k][i])continue; dfs(tr[k][i],k); int son=tr[k][i]; for(int j=m;j>=cost[k];j--) { for(int l=1;l+cost[k]<=j;l++) { dp[k][j]=max(dp[k][j],dp[son][l]+dp[k][j-l]); } } } } int main() { while(scanf("%d%d",&n,&m)!=EOF) { if(n==-1&&m==-1)break; // init(); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) { scanf("%d%d",&cost[i],&v[i]); cost[i]=cost[i]/20+(cost[i]%20!=0); // if(cost[i]%20!=0)cost[i]+=1; tr[i].clear(); } int a,b; for(int i=1;i<n;i++) { scanf("%d%d",&a,&b); tr[a].push_back(b); tr[b].push_back(a); } if(m==0){printf("0\n");continue;} dfs(1,-1); printf("%d\n",dp[1][m]); } return 0; }
CodeForces 161D Distance in Tree
题目链接:http://codeforces.com/contest/161/problem/D
简单树形dp:维护各个节点1到k的情况就可以了;
#include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <queue> #include <typeinfo> #include <map> #include <stack> typedef long long ll; using namespace std; inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } //************************************************************************************** ll ans,dp[50005][505];///以i为节点构成路径长度为j的种类树; vector<int >G[50005]; int fa[50005]; int vis[50005]; int n,m; void dfs(int k) { dp[k][0]=1; for(int i=0;i<G[k].size();i++){ int son=G[k][i]; if(vis[son])continue; vis[son]=1; dfs(son); for(int j=0;j<m;j++){ ans+=dp[k][m-j-1]*dp[son][j]; } for(int i=1;i<=m;i++) { dp[k][i]+=dp[son][i-1]; } } } int main() { scanf("%d%d",&n,&m); int a,b; for(int i=1;i<n;i++){ scanf("%d%d",&a,&b); G[a].push_back(b); G[b].push_back(a); } memset(vis,0,sizeof(vis)); memset(dp,0,sizeof(dp)); vis[1]=1; dfs(1); cout<<ans<<endl; return 0; }
【tyvj1520】树的直径
题目链接:http://www.tyvj.cn/p/1520
dp1[],dp2[]分别代表从当前根节点出发所能到达的最长链与次长链,感谢HZWER指点;。。。。。
///1085422276 #include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <queue> #include <typeinfo> #include <map> typedef long long ll; using namespace std; #define inf 10000000 inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } //*************************************************************** struct ss { int to,next,v; }e[10001*2]; int head[10001],t,dp1[10001],dp2[10001],ans; void add(int u,int v,int w) { e[t].to=v; e[t].next=head[u]; e[t].v=w; head[u]=t++; } void dp(int x,int pre) { for(int i=head[x];i;i=e[i].next) { if(e[i].to==pre)continue; dp(e[i].to,x); if(dp1[e[i].to]+e[i].v>dp1[x]) { dp2[x]=dp1[x]; dp1[x]=dp1[e[i].to]+e[i].v; } else dp2[x]=max(dp1[e[i].to]+e[i].v,dp2[x]); } ans=max(dp1[x]+dp2[x],ans); } int main() {int n; memset(head,0,sizeof(head)); t=1; ans=0; int u,v,w; scanf("%d",&n); for(int i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } dp(1,0); cout<<ans<<endl; return 0; }