几个树形dp
1.重建道路
树形dp基础题,f[i][j]表示在i这个点我和我的子树联通块大小为j最少砍几条边。
转移的时候,到下一个子树时上一个子树所有答案先++(此树直接砍掉不贡献答案),再继续dp。
注意更新答案时,如果不是跟答案还有+1(砍掉我和我父亲的),不然洛谷会挂四个点QAQ
马上就Noip了我还只能做这种水题QAQ 还挂
//Twenty #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> #include<queue> #include<ctime> typedef long long LL; using namespace std; const int N=201; int ans,dp[N][N],n,p; template<typename T> void read(T &x) { char ch=getchar(); T f=1; x=0; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f; } int ecnt,fir[N],nxt[N<<1],to[N<<1]; void add(int u,int v) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; } void dfs(int x,int fa) { dp[x][1]=0; for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { int y=to[i]; dfs(y,x); for(int j=p;j>=1;j--) { dp[x][j]++; for(int k=0;k<j;k++) dp[x][j]=min(dp[x][j],dp[x][j-k]+dp[y][k]); } } if(x==1) ans=min(ans,dp[x][p]); else ans=min(ans,dp[x][p]+1); } void work() { ans=1e9+7; memset(dp,127/3,sizeof(dp)); dfs(1,0); printf("%d\n",ans); } void init() { read(n); read(p); for(int i=1;i<n;i++) { int x,y; read(x); read(y); add(x,y); } } int main() { #ifdef DEBUG freopen(".in","r",stdin); freopen(".out","w",stdout); #endif init(); work(); return 0; }
2.P2014 选课
树形dp水题。
dp[i][j]表示在i这个点,在我和我的儿子中选了j门课的最大学分。注意转移时若i!=0自己必须选,所以枚举我儿子选的课必须小于我和我儿子选的所有课,而i==0时则小于等于都ok。
//Twenty #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> #include<queue> #include<ctime> typedef long long LL; using namespace std; const int maxn=307; int n,m,dp[maxn][maxn],f[maxn],v[maxn]; template<typename T> void read(T &x) { char ch=getchar(); T f=1; x=0; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f; } int ecnt,fir[maxn],nxt[maxn],to[maxn],ans; void add(int u,int v) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; } void dfs(int x) { dp[x][1]=v[x]; for(int i=fir[x];i;i=nxt[i]) { int y=to[i]; dfs(y); for(int j=m;j>=0;j--) for(int k=0;k<=(j-(x!=0));k++) dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]); } ans=max(ans,dp[x][m]); } void work() { dfs(0); printf("%d\n",ans); } void init() { read(n); read(m); for(int i=1;i<=n;i++) { read(f[i]); read(v[i]); add(f[i],i); } } int main() { #ifdef DEBUG freopen(".in","r",stdin); freopen(".out","w",stdout); #endif init(); work(); return 0; }
又是树形dp水题。。
跟上面两个没啥差,还是最简单的。没啥可说了。
好水呀。。
//Twenty #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> #include<queue> #include<ctime> typedef long long LL; using namespace std; const int maxn=100; int n,m,dp[maxn][maxn]; template<typename T> void read(T &x) { char ch=getchar(); T f=1; x=0; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f; } int ecnt,fir[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1],ans; void add(int u,int v,int w) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w; nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w; } void dfs(int x,int fa) { for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { int y=to[i]; dfs(y,x); for(int j=m;j>=0;j--) for(int k=0;k<j;k++) dp[x][j]=max(dp[x][j],dp[x][j-k-1]+dp[y][k]+val[i]); } } void work() { dfs(1,0); printf("%d\n",dp[1][m]); } void init() { read(n); read(m); for(int i=1;i<n;i++) { int x,y,z; read(x); read(y); read(z); add(x,y,z); } } int main() { #ifdef DEBUG freopen(".in","r",stdin); freopen(".out","w",stdout); #endif init(); work(); return 0; }
因为找题的时候已经知道是二分了,一开始有点懵逼,然后突然想起世界上有种叫01分数规划的东西,然后树形dp就ok了。
这个看起来是n^3的dp实际上似乎是n^2的,然后bzoj加了一组数据后先sz[x]+=sz[y]再转移就过不了了,然后改一下就卡过了。
//Twenty #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> #include<queue> #include<ctime> #define eps 1e-4 typedef long long LL; using namespace std; const int maxn=2507; int n,m,b[maxn],a[maxn],f[maxn]; double l,r=10000,dp[maxn][maxn]; template<typename T> void read(T &x) { char ch=getchar(); T f=1; x=0; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f; } int ecnt,fir[maxn],nxt[maxn],to[maxn],sz[maxn]; void add(int u,int v) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; } void dfs(int x,double now) { if(x) dp[x][1]=(double)a[x]-(double)b[x]*now; if(x!=0) sz[x]=1; else sz[x]=0; dp[x][0]=0; for(int i=fir[x];i;i=nxt[i]) { int y=to[i]; dfs(y,now); for(int j=sz[x];j>=(x!=0);j--) { for(int k=0;k<=sz[y];k++) dp[x][j+k]=max(dp[x][j+k],dp[x][j]+dp[y][k]); } sz[x]+=sz[y]; } } void work() { while(r-l>=eps) { double mid=(l+r)/2; memset(dp,0xc2,sizeof(dp)); dfs(0,mid); if(dp[0][m]>=eps) l=mid; else r=mid; } printf("%.3lf\n",l); } void init() { read(m); read(n); for(int i=1;i<=n;i++) { read(b[i]); read(a[i]); read(f[i]); add(f[i],i); } } int main() { #ifdef DEBUG freopen(".in","r",stdin); freopen(".out","w",stdout); #endif init(); work(); return 0; }