题解 P5969【POI2016 Nadajniki】
树形 dp。
思考转移的时候可以考虑开始只有一个点,每次加一棵子树进去,这样思考起来会清晰一点。
由于覆盖一条边的可能是相邻点或者其相邻边连向的点,所以处理时
记
不需要考虑
转移时,记当前节点为
先考虑
再考虑
-
如果
u 或v 上有发射器,则此边已被覆盖,直接不管; -
否则此边相邻点上的发射器数量为
u 儿子上发射器数量+ v 儿子上发射器数量,这两个都记在 dp 状态中了。根据这个更新u 状态的k 值。
还有就是根据
具体可以自己画张图思考一下,或者直接看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int inf;
int read()
{
int s=0;
char c=getchar(),lc='+';
while (c<'0'||'9'<c) lc=c,c=getchar();
while ('0'<=c&&c<='9') s=s*10+c-'0',c=getchar();
return lc=='-'?-s:s;
}
void write(int x)
{
if (x<0) putchar('-'),x=-x;
if (x<10) putchar(x+'0');
else write(x/10),putchar(x%10+'0');
}
void print(int x,char c='\n')
{
write(x);
putchar(c);
}
struct edge
{
int to,nxt;
}e[N*2];
int head[N],cnte=0;
void add_edge(int u,int v)
{
e[++cnte].to=v;
e[cnte].nxt=head[u];
head[u]=cnte;
}
int f[N][5][3],g[5][3];
bool trans(int x,int y,int a,int b,int &c,int &d)
{
c=x,d=y;
if (b)
{
if (x>2) return 0;
if (x<b) return 0;
}
if (1<=a&&a<=2) d=max(d-a,0);
if (!((1<=a&&a<=2)||(1<=x&&x<=2)))
{
int tot=max(a-2,0)+max(x-2,0);
d=max(d,2-tot);
}
if (1<=a&&a<=2&&!(1<=c&&c<=2)) c=min(a+max(c-2,0)+2,4);
return 1;
}
void dfs(int now,int father)
{
f[now][0][0]=0;
f[now][1][0]=1;
f[now][2][0]=2;
for (int i=head[now];i;i=e[i].nxt)
{
int to=e[i].to,c,d;
if (to==father) continue;
dfs(to,now);
memcpy(g,f[now],sizeof(g));
memset(f[now],0x3f,sizeof(f[now]));
for (int x=0;x<=4;x++)
for (int y=0;y<=2;y++)
if (g[x][y]<inf)
for (int a=0;a<=4;a++)
for (int b=0;b<=2;b++)
if (f[to][a][b]<inf)
if (trans(x,y,a,b,c,d))
f[now][c][d]=min(f[now][c][d],g[x][y]+f[to][a][b]);
}
}
signed main(signed bangumi,char *ss969[])
{
(void)bangumi,(void)ss969;
memset(f,0x3f,sizeof(f));
inf=f[0][0][0];
int n=read();
for (int i=1;i<n;i++)
{
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
dfs(1,0);
int ans=inf;
for (int i=0;i<=4;i++) ans=min(ans,f[1][i][0]);
print(ans);
return 0;
}