去学习了一下树形DP然后发现自己什么都不会!!!!!!
GGGGGGG。
首先是广为人知的一道
没有上司的舞会
emm没啥可说的,很久以前写的一道题
对于每一个节点有两种状态,选上司或选下属,分别做就好了。
因此
dp[i][0]=sum(max(dp[son][1],dp[son][0]));//显然,你不去,那下属就可以想去就去。
dp[i][1]=sum(dp[son][0])+happy[i];//显然你去了那下属就一定不能去。
#include<bits/stdc++.h> using namespace std; vector<int>son[10010]; int f[10010][2],v[10010],h[10010],n; void dp(int x){ f[x][0]=0; f[x][1]=h[x]; for(int i=0;i<son[x].size();i++){ int y=son[x][i]; dp(y); f[x][0]+=max(f[y][0],f[y][1]); f[x][1]+=f[y][0]; } } int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>h[i]; for(int i=1;i<n;i++){ int x,y; cin>>x>>y; v[x]=1; son[y].push_back(x); } int root; for(int i=1;i<=n;i++){ if(!v[i]){ root=i; break; } } dp(root); cout<<max(f[root][0],f[root][1])<<endl; }
然后是
最大子树和
#include<bits/stdc++.h> using namespace std; const int N=5211314; struct node{ int next,to; } e[N]; int head[N],cnt,n,dp[N],a[N],ans,val[N]; void add(int u,int v){ e[++cnt].next=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int u,int fa){ dp[u]=a[u]; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v!=fa){ dfs(v,u); dp[u]+=max(0,dp[v]); } } ans=max(ans,dp[u]); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v),add(v,u); } dfs(1,0); printf("%d ",ans); return 0; }
选课
#include<bits/stdc++.h> using namespace std; const int N=5211314; int n,m; struct node{ int next,to; } e[N]; int head[N],dp[5041][5041],cnt,a[N],val[N]; void add(int u,int v){ e[++cnt].next=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int u){ dp[u][1]=val[u]; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; dfs(v); for(int j=m;j>=1;j--){ for(int k=j-1;k>=1;k--){ dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]); } } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d%d",&a[i],&val[i]); add(a[i],i); } m++; dfs(0); printf("%d ",dp[0][m]); return 0; }
[USACO12FEB]附近的牛Nearby Cows
换根法好题
#include<bits/stdc++.h> using namespace std; struct node{ int to,next; }e[210001]; int head[100001],len; int f[100001][21],sons[100001];//状态,孩子数 int a,b,n,k; void build(){ e[++len].to=b; e[len].next=head[a]; head[a]=len; e[++len].to=a; e[len].next=head[b]; head[b]=len; sons[a]++;sons[b]++; } int main(){ int i,j,s; scanf("%d%d",&n,&k); for(i=1;i<n;i++){ scanf("%d%d",&a,&b); build(); } for(i=1;i<=n;i++)cin>>f[i][0]; for(j=1;j<=k;j++) for(i=1;i<=n;i++){ for(s=head[i];s;s=e[s].next) f[i][j]+=f[e[s].to][j-1]; if(j>1)f[i][j]-=(sons[i]-1)*f[i][j-2]; else f[i][1]+=f[i][0]; } for(i=1;i<=n;i++) printf("%d ",f[i][k]); return 0; }
还有一道也是换根法好题。
【POJ3585】Accumulation Degree
我才不会说我被卡了好久好久。。
#include<cstdio> #include<cstring> #include<algorithm> #define zero(x) memset(x,0,sizeof(x)) using namespace std; const int N=500001; struct node{ int next,to; } e[N*2]; inline int read() { int a = 0; char ch = getchar(); while (!(ch >= '0' && ch <= '9')) ch = getchar(); while (ch >= '0' && ch <= '9') { a = a * 10 + (ch - '0'); ch = getchar(); } return a; } bool vis[N]; int n,T; int head[N],in[N],val[N],cnt,dp[N],d[N]; void add(int u,int v,int z){ e[++cnt].next=head[u]; val[cnt]=z; e[cnt].to=v; head[u]=cnt; } void dps(int x){ vis[x]=1; for(int i=head[x];i;i=e[i].next){ int v=e[i].to; if(vis[v])continue; dps(v); if(in[v]==1){ d[x]+=val[i]; } else d[x]+=min(d[v],val[i]); } } void dfs(int x){ vis[x]=1; for(int i=head[x];i;i=e[i].next){ int v=e[i].to; if(vis[v])continue; if(in[x]==1)dp[v]=d[v]+val[i]; else dp[v]=d[v]+min(dp[x]-min(d[v],val[i]),val[i]); dfs(v); } } int main(){ int t=read(); while(t--){ zero(vis),zero(val),zero(dp),zero(d);zero(in); zero(e),zero(head); scanf("%d",&n);cnt=0; for(int i=1;i<n;i++){ int x=read(),y=read(),z=read(); in[x]++,in[y]++; add(x,y,z),add(y,x,z); } dps(1);memset(vis,0,sizeof(vis)); dp[1]=d[1]; dfs(1);int ans=0; for(int i=1;i<=n;i++) ans=max(ans,dp[i]);//求最大值 printf("%d ",ans);//输出 } }
暂时完结以后有新的再填吧。