P1269 信号放大器
链接
思路
这道题给的是一棵树,信号从 1 号服务器出发,要传到所有节点。信号每经过一条边,强度会减少这条边的长度。如果在某个节点安装信号放大器,就可以把当前仍然大于 0 的信号恢复成初始强度。
目标是让所有节点都能收到信号,并且安装的放大器数量最少。
首先有一个无解判断:如果存在一条边的长度大于等于初始强度 jd,那么无论怎么安装放大器都无法让信号通过这条边。因为信号到达边的另一端时强度必须大于 0,边长等于初始强度时,剩余强度正好为 0,也不合法。
所以代码中先用 dfs 遍历整棵树,同时记录每个节点的父亲:
void dfs(int u,int fa){
father[u]=fa;
for(edge edg:G[u]){
if(edg.id == fa)continue;
if(edg.val>=jd){
jdg=true;
}
dfs(edg.id,u);
}
}
如果发现 edg.val >= jd,直接输出 No solution.。
接下来考虑最少放多少个放大器。
设:
dp[u]
表示在 u 的子树中,从节点 u 往下走,在不额外从 u 的父边补信号的情况下,最远还需要覆盖到多长的路径。换句话说,dp[u] 记录的是 u 子树中最“危险”的那条向下路径长度。
对于节点 u,先递归处理所有儿子 v。如果从 u 继续往 v 的子树传信号,那么路径长度会变成:
dp[v] + len(u,v)
所以:
dp[u]=max(dp[u],dp[edg.id]+edg.val);
因为只要最远的那条路径能被覆盖,其它更短的路径也能被覆盖。
处理完所有儿子后,还要判断这条最远路径能不能继续通过 u 到父亲的那条边。如果:
dp[u] + uper > jd
说明如果不在 u 这里放一个放大器,那么从父亲方向传来的信号无法覆盖 u 子树中最远的需求。因此必须在 u 安装一个放大器:
if(dp[u]+uper>jd)dp[u]=0,ans++;
安装后,信号在 u 处被恢复成初始强度,所以 u 往下的剩余危险长度重新变成 0。
注意这里判断用的是 >,不是 >=。因为这一步表示“父亲方向到 u 后,再继续覆盖子树时是否超过初始强度”。如果刚好等于 jd,到达最远点时强度为 0,这个点已经被收到信号了;而单条边无解判断处必须用 >=,因为边的另一端需要收到强度大于 0 的信号,等于 0 不合法。
最后从根节点 1 开始做树形 DP,答案就是 ans。
易错
-
无解判断是
edg.val >= jd,不是edg.val > jd。边长等于初始强度时,信号到达另一端强度为0,不能被接收。 -
ddp中判断是否在当前点放放大器时,用的是:
if(dp[u]+uper>jd)
这里不要随手改成 >=,它和单条边无解判断的含义不一样。
uper是当前节点u到父亲的边长,需要在遍历邻接表时单独记录:
if(edg.id == father[u]){
uper=edg.val;
continue;
}
- 安装放大器后要把
dp[u]清零:
dp[u]=0;
因为信号在这里被恢复,往父亲方向看,这个子树已经不再继续累积危险距离。
- 输入给的是无根树,所以必须先从
1号节点 DFS,记录father,之后树形 DP 才能区分父亲和儿子。
时间复杂度为 O(n),每条边只会被遍历常数次。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define itn long long
#define tin long long
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
int jd;
bool jdg;
const int N=2e4+10;
struct edge{
itn id,val;
edge(){}
edge(int id,int val):id(id),val(val){};
};
vector<edge>G[N];
itn father[N];
int ans;
int dp[N];
void dfs(int u,int fa){
father[u]=fa;
for(edge edg:G[u]){
if(edg.id == fa)continue;
if(edg.val>=jd){
jdg=true;
}
dfs(edg.id,u);
}
}
void ddp(int u){
int uper=0;
for(edge edg:G[u]){
if(edg.id == father[u]){
uper=edg.val;
continue;
}
ddp(edg.id);
dp[u]=max(dp[u],dp[edg.id]+edg.val);
}
if(dp[u]+uper>jd)dp[u]=0,ans++;
// if(dp[u]>jd)jdg=true;
}
signed main(){
IOS;
int n;cin>>n;
for(itn i=1;i<=n;i++){
int k;cin>>k;
for(int j=1;j<=k;j++){
int id,val;cin>>id>>val;
G[i].push_back(edge(id,val));
}
}
cin>>jd;
dfs(1,0);
if(jdg){
cout<<"No solution.";
return 0;
}
ans=0;
ddp(1);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号