1.前言
我现在很气。
考试的时候想着写树形dp,结果因为脑残以为要多叉转二叉而写挂了。挂了之后我瞎搞一通,结果居然50分,我特别奇怪地加了个步数可以把整棵树走一边就输出n的特判,就蜜汁AC了,这!什!么!鬼!题!!!!!!
好吧,后来发现可以证明我的贪心是对的,也就是说树形dp什么的完全是我想多了。
反正我觉得我要滚粗了,连重庆的水题都做不出。
2.贪(xia)心(gao)做法(100分,O(n))
可以证明,除了树上最长链以外的每一个节点都可以”看作“两步到达。感性的思路就是:走到最长链上是不要返回的,假如最优解中还要经过一些非最长链上的点,先走最长链,走到可以到达这些点的地方走一遍这些点,返回,因为要返回所以两步到达,在最长链上不返回肯定最优。(理性的思路:安大神犇居然还在坐标系里建立关于深度的函数证明了,反正我弱我不会)
所以我们就得到了贪心做法:
1.走不完最长链,就是步数
2.走的完整棵树,就是n
3.否则就是(步数-最长链)/2+最长链
#include<iostream>
#include<cstdio>
#include<climits>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<vector>
using namespace std;
vector<int>lu[105];
int deep[105];
bool vis[105];
int n,v,ans,bj,mx=0;
int read(){
char ch=' ';int w=1,p=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')p=p*10+ch-'0',ch=getchar();
return p*w;
}
void dfs(int x,int d,int las){
int sz=lu[x].size(),i;
mx=max(d,mx);deep[x]=d;
for(i=0;i<sz;i++)
if(lu[x][i]!=las)dfs(lu[x][i],d+1,x);
}
int main()
{
int i,j,x,y;
n=read();v=read();
for(i=1;i<=n-1;i++){
x=read();y=read();
lu[x+1].push_back(y+1);lu[y+1].push_back(x+1);
}
dfs(1,1,0);
if(v+1<=mx){printf("%d",v+1);return 0;}
ans=mx+(v-mx+1)/2;
printf("%d",min(ans,n));
return 0;
}
//可以证明,除了最长链以外的每一个点都可以看作两步到达
3.树形dp做法(100分,O(nm^2))
树形dp做法就比较直观了,这题连多叉转二叉都用不上,直接一个树上的01背包搞定。
有三种走法:
1.左边走一些链并回来,右边走一些链不回来
2.左边走一些链不回来,右边走一些链并回来
3.左边走一些链回来,右边走一些链回来
注意到最后对于每一个步数,里面存的值应该赋值为小于这个步数的所有步数最优值
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<vector>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
int g[105][105],f[105][105];//g:回来,f:不回来
int read(){
char ch=' ';int w=1,q=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
return w*q;
}
int n,v;
vector<int>lu[105];
void dfs(int x,int las){
int i,j,k,sz=lu[x].size();
g[x][0]=f[x][0]=1;
for(i=0;i<sz;i++){
if(lu[x][i]==las)continue;
dfs(lu[x][i],x);
for(j=v;j>=1;j--){//树上的01背包
for(k=1;k<=j;k++){
if(k>=2)g[x][j]=max(g[x][j],g[lu[x][i]][k-2]+g[x][j-k]);//左边回右边回
f[x][j]=max(f[x][j],f[lu[x][i]][k-1]+g[x][j-k]);//左边不回右边回
if(k>=2)f[x][j]=max(f[x][j],g[lu[x][i]][k-2]+f[x][j-k]);//左边回右边不回
}
}
}
for(i=2;i<=v;i++)g[x][i]=max(g[x][i-1],g[x][i]);//注意这里!!!不一定要步数正好一样
for(i=2;i<=v;i++)f[x][i]=max(f[x][i-1],f[x][i]);
}
int main()
{
int i,j,x,y;
n=read();v=read();
for(i=1;i<=n-1;i++){
x=read();y=read();
lu[x+1].push_back(y+1);lu[y+1].push_back(x+1);
}
dfs(1,0);
printf("%d",f[1][v]);
return 0;
}
4.后记
我要滚粗了~这次省选绝对挂了~
我好弱呀怎么办呀~~~~~