题目
{% raw %}
{% raw %}
{% endraw %}
输入包含多组数据
每组数据第一行包含一个n(2?≤?n?≤?10^5),表示有n座房子
之后n-1行,每行三个数字a,b,c表示在房子a和房子b之间有一条路,c等于1表示路已被打扫,c等于2表示路未被打扫。
{% raw %}
{% endraw %}
每组数据输出一个整数k,表示最少要给k个朋友打电话
{% raw %}
{% endraw %}
5
1 2 2
1 3 2
1 4 2
1 5 2
{% raw %}
{% endraw %}
4
{% raw %}
题解
根据题意画出图像,可以非常容易发现最终形成一棵以西瓜为树根的树
朋友会一路清扫倒西瓜所在的房间,因此只有住在"树叶"的朋友有可能清扫
根据这个思路来分析每一个节点
可以发现:
- 如果从一个节点的子节点已经有
a
个朋友清扫,那么从该节点到树根都不需要再找朋友清扫 - 如果该节点的所有子节点都没有朋友清扫,那么如果从该节点到树根有需要清扫的路,只需要找一个朋友清扫即可
因此可以发现,对于每一个节点,从树叶到该节点所需的最少朋友数只与其子节点有关,符合 动态规划 的特点,属于 树形dp
用 dp[i]
表示从叶子到第 i
个节点的所需要最少的朋友数, dp[1]
即为答案
有 dp[i] = sum{ max(dp[j],Need_Clean) }
其中 j
为 i
的所有子节点, Need_Clean
为道路 (i,j)
是否需要清扫
只需要一个 DFS 即可解决
代码
-
#include
const int maxn = 1e5+5;
struct path{
int u,v;
bool k;
path(int a,int b,int c):u(a),v(b),k(c){}
};
struct Node{
list
int father;
};
Node node[maxn];
int dfs(int t){
int ans=0;
for(list
int tt = it->v;
if(tt == node[t].father)
continue;
node[tt].father = t;
ans += max(dfs(tt),(int)it->k);
}
return ans;
}
int main(){
cin.tie(0);
cin.sync_with_stdio(false);
int n;
while(cin >> n){
for(int i=1;i<=n;i++){
node[i].father=-1;
node[i].children.clear();
}
int a,b,c;
for(int i=1;i<n;i++){
cin>>a>>b>>c;
node[a].children.push_back(path(a,b,c-1));
node[b].children.push_back(path(b,a,c-1));
}
cout << dfs(1) << endl;
}
}
</div>