树形DP(1)
一、P1352 没有上司的舞会
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 6010 #define inf 0x3f3f3f3f int n,w[maxn],head[maxn],dp[maxn][2],vis[maxn][2],ant; struct Egde{ int v,next; }edge[maxn]; void adde(int u,int v){ edge[ant].v=v,edge[ant].next=head[u]; head[u]=ant++; } void dfs(int n,int choice){ vis[n][choice]=1; //printf("---%d\n",n); if(choice==1) dp[n][1]=w[n]; for(int i=head[n];i!=0;i=edge[i].next){ int v=edge[i].v; //printf("%d %d\n",n,v); if(choice==0){ if(vis[v][1]==0) dfs(v,1); if(vis[v][0]==0) dfs(v,0); dp[n][0]+=max(dp[v][1],dp[v][0]); } else{ if(vis[v][0]==0) dfs(v,0); dp[n][1]+=dp[v][0]; } } } int main(){ scanf("%d",&n); ant=1; for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&v,&u); adde(u,v); } int ans=0; for(int i=1;i<=n;i++){ if(vis[i][0]==0) dfs(i,0); if(vis[i][1]==0) dfs(i,1); //printf("%d %d\n",dp[i][0],dp[i][1]); ans=max(ans,max(dp[i][0],dp[i][1])); } printf("%d\n",ans); return 0; }
ant一开始默认0,但是head没有赋为-1,卡了一下~习惯还是不要乱改的好
二、P2015 二叉苹果树
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 110 #define inf 0x3f3f3f3f int N,Q,dp[maxn][maxn],ant,head[maxn],vis[maxn]; struct Edge{ int v,next,w; }edge[maxn*2]; void adde(int u,int v,int w){ edge[ant].v=v,edge[ant].w=w; edge[ant].next=head[u],head[u]=ant++; } void dfs(int u){ vis[u]=1; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v,w=edge[i].w; if(vis[v]) continue; dfs(v); for(int i=Q;i>=1;i--){ for(int j=1;j<=i;j++){ dp[u][i]=max(dp[u][i],dp[u][i-j]+w+dp[v][j-1]); } } } } int main(){ memset(head,-1,sizeof(head)); scanf("%d%d",&N,&Q); for(int i=1;i<N;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); adde(u,v,w); adde(v,u,w); } dfs(1); printf("%d\n",dp[1][Q]); return 0; }
一开始错误代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 110 #define inf 0x3f3f3f3f int N,Q,dp[maxn][maxn],ant,head[maxn],head2[maxn],vis[maxn]; struct Edge{ int v,next,w; }edge[maxn*3]; void adde(int u,int v,int w){ edge[ant].v=v,edge[ant].w=w; edge[ant].next=head[u],head[u]=ant++; } void adde2(int u,int v,int w){ edge[ant].v=v,edge[ant].w=w; edge[ant].next=head2[u],head2[u]=ant++; } void dfs(int n,int q){ for(int i=head[n];i!=-1;i=edge[i].next){ int v=edge[i].v,w=edge[i].w; for(int j=1;j<=q;j++){ dp[n][q]=max(dp[n][q],dp[n][q-j]+dp[v][j-1]+w); } //printf("%d %d %d %d %d\n",n,q,v,w,dp[n][q]); } } void dfs2(int u){ vis[u]=1; for(int i=head2[u];i!=-1;i=edge[i].next){ int v=edge[i].v,w=edge[i].w; if(!vis[v]){ adde(u,v,w); dfs2(v); } } } int main(){ memset(head,-1,sizeof(head)); memset(head2,-1,sizeof(head2)); scanf("%d%d",&N,&Q); for(int i=1;i<N;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); adde2(u,v,w); adde2(v,u,w); } dfs2(1); for(int i=1;i<=Q;i++){ for(int j=1;j<=N;j++){ dfs(j,i); //printf("%d %d %d\n",j,i,dp[j][i]); } } printf("%d\n",dp[1][Q]); return 0; }

样例1都没过( ╯□╰ )

算出来=200?
原来的代码不能控制二叉树的左右两个方向,所以应该以点dfs遍历,这样就能控制先左后右。
而不是以树枝数量为起点。
树上dp的无后性要保证每次dp的时候影响右树枝的只有左树枝,所以dp的时候i要从大到小。
三、P2014 [CTSC1997]选课
和第二题一模一样,写了2小时……………… 菜是原罪

#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 310 int N,M,head[maxn],score[maxn],ant,account[maxn],dp[maxn][maxn]; struct Edge{ int v,next; }edge[maxn]; void adde(int u,int v){ edge[ant].v=v,edge[ant].next=head[u]; head[u]=ant++; } void dfs(int n){ for(int i=head[n];i!=-1;i=edge[i].next){ int v=edge[i].v; dfs(v); for(int j=M;j>=1;j--){ for(int k=1;k<=j;k++){ dp[n][j]=max(dp[n][j],dp[n][j-k]+score[v]+dp[v][k-1]); //dp[n][j]=max(dp[n][j],dp[n][j-k]+dp[v][k]); } } } } int main(){ memset(head,-1,sizeof(head)); scanf("%d%d",&N,&M); for(int i=1;i<=N;i++){ int u,w; scanf("%d%d",&u,&w); score[i]=w; if(u){ adde(u,i); account[i]++; } } for(int i=1;i<=N;i++){ if(!account[i]) adde(0,i); } dfs(0); printf("%d\n",dp[0][M]); return 0; }
四、P1613 跑路
不会,先来个看篇题解给大脑降降温
【首先说一下一个明显的错误: 绝对不能直接求最短路
原因很简单,题目中有说,有跑路器。
那么进一步思考,发现,任意 $$2^k$$ 的路径长度都可以在1秒内走完,因此如果有 u v 两点间的最短距离为 $$2^k$$ 即可认为这两点之间有一条边(本题中边权全部为1)。
接下来,思路就很简单了。令 $$f_{i,j,k}$$ 的意义为能否在 $$2^i$$ 时间内从点 j 走到点 k ,可以为1,不可以为0。进一步,由倍增的思想可以得到:若 $$f_{i-1, v, k}$$ 和 $$f_{i-1, k, u}$$ 同时为1,则 $$f_{i, v, u}$$ 为1。
在这些都预处理完成之后,在跑一个最短路,就正确了。】---洛谷第二题解
卡60分卡了一小时,输入时忘了加dis[u][v]=1。淦
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 55 #define maxm 10010 #define inf 0x3f3f3f3f int dp[66][maxn][maxn],n,m,dis[maxn][maxn]; int main(){ memset(dis,inf,sizeof(dis)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); dp[0][u][v]=1; dis[u][v]=1; } for(int t=0;t<=64;t++){ for(int i=1;i<=n;i++){ for(int k=1;k<=n;k++){ for(int j=1;j<=n;j++){ if(dp[t][i][k]&&dp[t][k][j]){ dp[t+1][i][j]=1; dis[i][j]=1; //printf("---%d %d\n",i,j); } } } } } for(int k=1;k<=n;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); } } } printf("%d\n",dis[1][n]); return 0; }

浙公网安备 33010602011771号