[NOIP模拟赛Day1]轰炸

来源:互联网 发布:浪潮税控开票软件 编辑:程序博客网 时间:2024/04/25 12:53
【题目描述】
A国和B国开战。B国国王痴迷于树,所以B国的城市道路是一棵树的形状。A国情报局通过B国地下组织获取了该消息……给力的地下党还窃取了B国每一个城市的炸药储备量m[i],即轰炸城市i可以同时炸掉距离城市i在m[i]之内的其他城市。但爆炸不是连续的。
由于A国国力有限,A国情报局局长想知道至少需要炸几次才能把B国所有城市都炸掉……
【输入格式】
第一行输入一个整数n,表示B国的城市数量。
第二行输入n个整数,第i个整数m[i]表示城市i的炸药储备量。
接下来的n-1行,每一行输入两个整数u、v,表示一条连接城市u、v长度为的城市道路。
【输出格式】
输出一行一个整数,即最少轰炸次数。
【样例输入】
5
1 1 1 1 1
1 2
2 3
3 4
4 5
【样例输出】
2
【数据范围】
对于10%的数据,n≤10。
对于30%的数据,n≤1000。

对于100%的数据,n≤10^5,m[i]≤100。



题解:一道典型的树型DP。

f[r][i]表示以r为根的子树被完全破坏,同时还能向上延伸i的最小值。

g[r][i]表示以r为根的子树未被完全破坏,还应向下延伸i的最小值。

转移方程式:

①不轰炸r节点

 f[r][i]=min( f[son][i+1]+Σ min( f[otherson][0~i+1], g[otherson][0~i-1] ) )

 g[r][i]=min( g[son][i-1]+Σ min( f[otherson][0~i], g[otherson][0~i-1] ) )

②轰炸r节点

f[r][ m[r] ]=min( f[r][ m[r] ], 1+Σ min( f[son][ 0~m[r]+1 ], g[son][ 0~m[r]-1 ] ) )


很显然直接这样做是会TLE的。


minf[r][i]表示min( f[r][0~i] ),同理ming[r][i]表示min( g[r][0~i] );

再用s1[r][i]表示Σ min( f[son][0~i+1], g[son][0~i-1] ),

s2[r][i]表示Σ min( f[son][0~i], g[son][0~i-1] ) ;

那么上述转移方程式可以写成:

f[r][i]=min( s1[r][i]+f[son][i+1]-min( f[son][i+1], g[son][i-1] ) )

g[r][i]=min( s2[r][i]+g[son][i-1]-min( f[son][i], g[son][i-1] ) )


这样时间复杂度为O( 100*n )。


#include<iostream>#include<cstdio>using namespace std;const int N=1e5+10;const int M=105;void Getin( int &shu ) {char c; int f=1; shu=0;for( c=getchar(); c<'0' || c>'9'; c=getchar() ) if( c=='-' ) f=-1;for( ; c>='0' && c<='9'; c=getchar() ) shu=shu*10+c-'0';shu*=f;}int fir[N], ecnt;struct node{ int e, next; }edge[N<<1];void Link( int s, int e ) {edge[++ecnt].e=e; edge[ecnt].next=fir[s]; fir[s]=ecnt;edge[++ecnt].e=s; edge[ecnt].next=fir[e]; fir[e]=ecnt;}int n, m[N], maxm, s, e;int f[N][M], g[N][M], minf[N][M], ming[N][M];int s1[N][M], s2[N][M];void DP( int r, int fa ) {for( int i=fir[r]; i; i=edge[i].next )if( edge[i].e!=fa ) {DP( edge[i].e, r );s1[r][0]+=minf[ edge[i].e ][1];s2[r][0]+=minf[ edge[i].e ][0];for( int j=1; j<=maxm; j++ )s1[r][j]+=min( minf[ edge[i].e ][j+1], ming[ edge[i].e ][j-1] ),s2[r][j]+=min( ming[ edge[i].e ][j-1], minf[ edge[i].e ][j] );}for( int i=maxm+1; i>=0; i-- )f[r][i]=g[r][i]=minf[r][i]=ming[r][i]=n;g[r][0]=s2[r][0];for( int i=fir[r]; i; i=edge[i].next )if( edge[i].e!=fa ) {f[r][0]=min( f[r][0], s1[r][0]+f[ edge[i].e ][1]-minf[ edge[i].e ][1] );for( int j=1; j<=maxm; j++ )f[r][j]=min( f[r][j], s1[r][j]+f[ edge[i].e ][j+1]-min( minf[ edge[i].e ][j+1], ming[ edge[i].e ][j-1] ) ),g[r][j]=min( g[r][j], s2[r][j]+g[ edge[i].e ][j-1]-min( ming[ edge[i].e ][j-1], minf[ edge[i].e ][j] ) );}f[r][ m[r] ]=min( f[r][ m[r] ], s1[r][ m[r] ]+1 );minf[r][0]=f[r][0]; ming[r][0]=g[r][0];for( int i=1; i<=maxm+1; i++ )minf[r][i]=min( f[r][i], minf[r][i-1] ),ming[r][i]=min( g[r][i], ming[r][i-1] );}int main() {Getin(n);for( int i=1; i<=n; i++ ) Getin( m[i] ), maxm=max( maxm, m[i] );for( int i=1; i<n; i++ )Getin(s), Getin(e), Link( s, e );DP( 1, -1 );printf( "%d\n", minf[1][maxm] );return 0;}




原创粉丝点击