洛谷p2015绿
题意:有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点)这棵树共有 N个结点(叶子点或者树枝分叉点),编号为 1∼𝑁,树根编号一定是 1。现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。给定需要保留的树枝数量,求出最多能留住多少苹果。
分析:写出状态转移方程fx=max(fx,fj+fx) 。j是儿子。直接让他们初始为applei就行了,后面就不用加了。
代码:
#include<bits/stdc++.h> using namespace std; int n,m,a,b,v; int edge[1000],ne[1000],apple[1000],last[1000],f[1000][1000],cnt=1; void s(){ for(int i=1;i<=n;i++)last[i]=-1; } void add(int a,int b,int v){ edge[cnt]=b; ne[cnt]=last[a]; apple[cnt]=v; last[a]=cnt++; } void dfs(int x,int y){ for(int i=last[x];i>=1;i=ne[i]){ int j=edge[i];//j是i的儿子; if(j==y)continue; f[j][1]=apple[i];//存储子节点到父节点的值 dfs(j,x);//进入下一层搜索 for(int z=m;z>=1;z--){ for(int k=0;k<=z;k++){ if((k!=z&&z!=1)||x==1)f[x][z]=max(f[x][z],f[j][k]+f[x][z-k]); } } } } int main(){ cin>>n>>m;s(); for(int i=1;i<n;i++){ cin>>a>>b>>v; add(a,b,v);//a是i的父亲节点 add(b,a,v); } dfs(1,0); cout<<f[1][m]; }
洛谷p1270蓝
题意:第一行是警察赶到的时间,以秒为单位。第 2行描述了艺术馆的结构,是一串非负整数,成对地出现:每一对的第一个数是走过一条走廊的时间,第 2 个数是它末端的藏画数量;如果第 2个数是 0,那么说明这条走廊分叉为两条另外的走廊.
分析:用dfs+树形dp。建树,然后dfs搜到底,用dp求出偷到的画
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=20005; //空间开大点 struct edge{ int v,w,next; }e[maxn]; int head[maxn],cnt; void add(int u,int v,int w){ //存边 e[++cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; } int n,tot,root,ans; int dp[maxn][maxn]; //dp[i][j]表示以i为根的子树,消耗j秒能拿到的画的最大值 int size[maxn]; void insert(int x,int fa){ //建图 int time,pic; //上个点走到这个点的时间 ,这个点画的数量 scanf("%d %d",&time,&pic); add(fa,x,time<<1); //将父结点连到该节点 if(!pic){ //分成两条路 insert(++tot,x); //tot是结点序号 insert(++tot,x); } else{ //叶子节点 while(pic--) //每有一幅画,连一条边到0号结点 add(x,0,5); } } void dfs(int u){ for(int i=head[u];i;i=e[i].next){ int v=e[i].v,w=e[i].w; dfs(v); size[u]+=size[v]+w; for(int j=min(n,size[u]);j>=w;j--) //j秒的时间在u子树 for(int k=0;k<=min(j-w,size[v]);k++) //k秒的时间走到v子树 dp[u][j]=max(dp[u][j],dp[u][j-k-w]+dp[v][k]); } } int main(){ scanf("%d",&n);n--; root=tot=1; insert(++tot,root); //以root=1为起始节点 dp[0][0]=1; dfs(root); for(int i=1;i<n;i++) ans=max(ans,dp[root][i]); printf("%d",ans); return 0; }
P3360 偷天换日(蓝)
题意:艺术馆由若干个展览厅和若干条走廊组成。每一条走廊的尽头不是通向一个展览厅,就是分为两个走廊。每个展览厅内都有若干幅画,每副画都有一个价值。经过走廊和偷画都是要耗费时间的。警察会在 n 秒后到达进口,在不被逮捕的情况下你最多能得到的价值。
分析:如果x是0,就合并两棵树,并求背包dp,否则就开始偷画,偷画也要时间,用dp算最多能偷多少画。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e3+20; ll n,tot=1; ll dp[N][N],w[N],c[N]; void dfs(int now){ ll t,x; cin>>t>>x;t<<=1; if(x){ for(ll i=1;i<=x;i++){ cin>>w[i]>>c[i]; } for(ll i=1;i<=x;i++){ for(ll j=n;j>=c[i];j--){//背包容量 if(j-c[i]>=t)dp[now][j]=max(dp[now][j],dp[now][j-c[i]]+w[i]); } //cout<<dp[now][n]; } return; } //如果x为0 ll l=++tot; dfs(tot); ll r=++tot; dfs(tot); //合并左右子树的状态 for(ll i=t;i<=n;i++){ for(ll j=0;j<=i-t;j++){ dp[now][i]=max(dp[now][i],dp[l][j]+dp[r][i-t-j]); } } } int main(){ cin>>n; n--; dfs(1); cout<<dp[1][n]; return 0; }