题目大意:
给你一个$n(nle100)$个点的树和一个数$m$,问从给定的点出发,任意游走$m(mle100)$次最多能走过几个点(重复经过点不计入答案)?
思路:
一眼看上去有种贪心的感觉。看Status里面一堆0ms的,AC率又那么高,果断写了一个$O(n)$贪心,每次贪心地走完最深的子树,结果交上去才30分,发现有时子树走一半就上去反而更优。
考虑一个$O(n^3)$的DP。用$f[i][j]$表示$i$结点为根的子树走$j$次最后回到$i$最多能经过的点数,$g[i][j]$表示$i$结点为根的子树走$j$次步数用尽不回到$i$最多能经过的点数。转移方程显然:
$f[x][i]=max{f[x][i-j-2]+f[y][j]}$
$g[x][i]=max(max{f[x][i-j-1]+g[y][j]},max{g[x][i-j-2]+f[y][j]})$
交一发80ms,看题解后发现那些0ms的人其实用的也是贪心,枚举结束位置的点,让剩下的步数尽量用完,即答案为$frac{min(max{dep[i]},m)+m}2+1$。时间复杂度$O(n)$。
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<algorithm> 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 const int N=100,M=200; 13 int m,h[N],sz,f[N][N+1],g[N][N+1]; 14 struct Edge { 15 int to,next; 16 }; 17 Edge e[M]; 18 inline void add_edge(const int &u,const int &v) { 19 e[sz]=(Edge){v,h[u]};h[u]=sz++; 20 e[sz]=(Edge){u,h[v]};h[v]=sz++; 21 } 22 inline void dfs(const int &x,const int &par) { 23 f[x][0]=g[x][0]=1; 24 for(int i=h[x];~i;i=e[i].next) { 25 const int &y=e[i].to; 26 if(y==par) continue; 27 dfs(y,x); 28 for(register int i=m;i;i--) { 29 for(register int j=0;j<i;j++) { 30 if(i-j-2>=0) { 31 f[x][i]=std::max(f[x][i],f[x][i-j-2]+f[y][j]); 32 g[x][i]=std::max(g[x][i],g[x][i-j-2]+f[y][j]); 33 } 34 g[x][i]=std::max(g[x][i],f[x][i-j-1]+g[y][j]); 35 } 36 } 37 } 38 for(register int i=1;i<=m;i++) { 39 f[x][i]=std::max(f[x][i],f[x][i-1]); 40 g[x][i]=std::max(g[x][i],g[x][i-1]); 41 } 42 } 43 int main() { 44 memset(h,-1,sizeof h); 45 const int n=getint();m=getint(); 46 for(register int i=1;i<n;i++) { 47 add_edge(getint(),getint()); 48 } 49 dfs(0,0); 50 printf("%d ",g[0][m]); 51 return 0; 52 }