目录
二:求最小点覆盖----树形dp
三:求最大独立集----树形dp
四:I - Tree Cutting (Easy Version)
一:黑白树(牛客网)
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
一棵n个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点i对应一个值k[i]。每个点都有一个颜色,初始的时候所有点都是白色的。
你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点i,i必须是白色的,然后i到根的链上(包括节点i与根)所有与节点i距离小于k[i]的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。输入描述:
第一行一个整数n (1 ≤ n ≤ 10^5) 接下来n-1行,每行一个整数,依次为2号点到n号点父亲的编号。 最后一行n个整数为k[i] (1 ≤ k[i] ≤ 10^5) 样例解释: 对节点3操作,导致节点2与节点3变黑 对节点4操作,导致节点4变黑 对节点1操作,导致节点1变黑
输出描述:
一个数表示最少操作次数
示例1
输入
复制
4 1 2 1 1 2 2 1
输出
复制
3
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int n;
vector<int> v[N];
int k[N],fa[N];
int ans=0;
int dfs(int x){
//now表示剩余的染色距离
int now = 0, siz = v[x].size();
for(int i=0;i<siz;i++)
now=max(now,dfs(v[x][i]));
//该点染不到,就计数,返回该点所能到达的最大染色距离
if(now<=0){
ans++;
return k[x]-1;
}
//回溯过程更新父节点的染色距离已达到最大染色距离
k[fa[x]]=max(k[fa[x]],k[x]-1);
return now-1;
}
int main(){
int n;
scanf("%d", &n);
for(int i=2;i<=n;i++){
int d;
scanf("%d", &d);
v[d].push_back(i);
fa[i]=d;
}
for(int i=1;i<=n;i++)
scanf("%d", &k[i]);
dfs(1);
printf("%d\n",ans);
}
二:求最小点覆盖----树形dp
鲍勃喜欢玩电脑游戏,特别是战略游戏,但有时他找不到足够快的解决办法,然后他很难过。现在他有了以下问题。他必须保卫一座中世纪的城市,这座城市的道路形成了一棵树。他必须把最小数量的士兵放在节点上,这样他们才能观察到所有的边缘。你能帮他吗?
您的程序应该找到Bob必须为给定的树设置的最小士兵数。
例如,对于树:
解决方案是一名士兵(在节点1处)。输入
输入包含几个文本格式的数据集。每个数据集表示一棵树,描述如下:
- 节点数
- 以下列格式描述每个节点
Node_标识符:(Number_Of_Road)节点_标识符1节点标识符2..节点标识符道路数目
或
节点_标识符:(0)
节点标识符为0到n-1之间的整数,对于n个节点(0<n<=1500);每一行输入中的道路数不超过10。输入数据中每条边只出现一次。输出量
输出应打印在标准输出上。对于每个给定的输入数据集,在给出结果的一行中打印一个整数(士兵的最小数量)。举例如下:
样本输入
4 0:(1) 1 1:(2) 2 3 2:(0) 3:(0) 5 3:(3) 1 4 2 1:(1) 0 2:(0) 0:(0) 4:(0)
样本输出
1 2
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
//求一棵树的最小点覆盖
//dp[u][0]表示节点u属于点覆盖集合
//dp[u][1]表示节点u不属于点覆盖集合
//连个重要的状态转移方程
//dp[u][0]+=min(dp[v][0],dp[v][1]);
//dp[u][1]+=dp[v][0];
ll dp[1500][2];
vector<ll>p[1500];
void dfs(ll u,ll pa){
//初始化
dp[u][0]=1;
dp[u][1]=0;
ll len=p[u].size();
for(ll i=0;i<len;i++){
ll v=p[u][i];
//如果找到自己的父节点,不做处理
if(v==pa)continue;
dfs(v,u);//算出以v为根节点的情况来更新父节点u
dp[u][0]+=min(dp[v][0],dp[v][1]);
dp[u][1]+=dp[v][0];
}
}
int main(){
ll n;
while(scanf("%lld",&n)!=EOF){
for(ll i=0;i<n;i++){
p[i].clear();
}
ll x,t;
for(ll i=0;i<n;i++){
scanf("%lld:(%lld)",&x,&t);
for(ll j=0;j<t;j++){
ll y;
scanf("%lld",&y);
p[x].push_back(y);
p[y].push_back(x);
}
}
memset(dp,0,sizeof(dp));
dfs(0,0);
cout<<min(dp[0][0],dp[0][1])<<endl;
}
return 0;
}
三:求最大独立集----树形dp
亲爱的选手,
我将在我的别墅在Hali-Bula举行一个聚会,以庆祝我从业连管退休。我希望我能邀请我所有的同事,但是想象一下,当一个员工在客人中找到他的老板时,他会怎样享受一个聚会!所以,我决定不邀请员工和他/她的老板。在业连管的组织层次是这样的,没有人有超过一个老板,而且只有一个员工没有老板(大老板)!我可以请你写一个程序来确定客人的最大数量,这样当他/她的老板也被邀请的时候,就不会有员工被邀请了吗?我已经附上了员工名单和业连管的组织等级。
最好,
-布莱恩·班尼特如果你的节目能说明如果我选择邀请有这种条件的客人的最大数量,那么这个名单是否是唯一确定的,我将不胜感激。
输入
输入由多个测试用例组成。每个测试用例都由一个包含整数的行开始。n(1≤)n(≤200),业连管的雇员人数。下一行只包含“大老板”的名称。以下每一项n-一行包括雇员的姓名和老板的姓名。所有的名字都是至少一个字串,最多100个字母,用空格隔开。每个测试用例的最后一行包含一个0。
输出量
对于每个测试用例,编写一行,其中包含一个数字,指示根据所需条件可以邀请的最大来宾数,以及一个单词“是”或“否”,这取决于在这种情况下来宾列表是否是唯一的。
样本输入
6 Jason Jack Jason Joe Jack Jill Jason John Jack Jim Jill 2 Ming Cho Ming 0
样本输出
4 Yes 1 No
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
vector<ll> p[201];
//dp[i][0]表示不取i节点
//dp[i][1]表示取i节点
//dup[i][0]表示不取i节点是否唯一,等于0表示不唯一,等于1表示唯一
//dup[i][1]表示取i节点是否唯一,,,,,,
ll dp[201][2],dup[201][2];
map<string,ll> mp;
//在一棵树中找不相邻的节点,求最大的数量,并判断方案是否唯一
//找最大独立集问题
void dfs(ll root){
dp[root][1]=1;
dp[root][0]=0;
dup[root][0]=1;
dup[root][1]=1;
ll len=p[root].size();
for(ll j=0;j<len;j++){
ll u=p[root][j];
dfs(u);
dp[root][0]+=max(dp[u][0],dp[u][1]);
dp[root][1]+=dp[u][0];
//判唯一性:根据子节点的情况决定
//不选root节点的唯一性判断
if(dp[u][0]>dp[u][1]&&dup[u][0]==0){
dup[root][0]=0;
}
else if(dp[u][1]>dp[u][0]&&dup[u][1]==0){
dup[root][0]=0;
}
else if(dp[u][0]==dp[u][1]){
dup[root][0]=0;
}
//选root节点的唯一性判断
if(dup[u][0]==0){
dup[root][1]=0;
}
}
}
int main(){
ll n;
while(cin>>n&&n){
for(ll i=1;i<=200;i++){
p[i].clear();
}
mp.clear();
string a,b;
cin>>a;
ll cnt=1;
if(!mp[a]){
mp[a]=cnt++;
}
for(ll i=1;i<n;i++){
cin>>a>>b;
if(!mp[a]){
mp[a]=cnt++;
}
if(!mp[b]){
mp[b]=cnt++;
}
p[mp[b]].push_back(mp[a]);
}
memset(dp,0,sizeof(dp));
memset(dup,0,sizeof(dup));
dfs(1);
//求最优方案:max(dp[1][0],dp[1][1]);
if(dp[1][0]>dp[1][1]&&dup[1][0]==1){
cout<<dp[1][0]<<" Yes"<<endl;
}
else if(dp[1][1]>dp[1][0]&&dup[1][1]==1){
cout<<dp[1][1]<<" Yes"<<endl;
}
else cout<<max(dp[1][1],dp[1][0])<<" No"<<endl;
}
return 0;
}
三: I - Tree Cutting (Easy Version)----链接
输出量
标准输出
给你一棵没有方向的树nn顶点。
有些顶点是蓝色的,有些是红色的,有些是未着色的。保证树包含至少一个红色顶点和至少一个蓝色顶点。
你选择一个边缘并将它从树中移除。树被分成两个相连的组件。让我们说一句“边缘”好的,漂亮的如果两个结果组件都不包含红色和蓝色的顶点。
多少好的,漂亮的在给定的树中有边缘吗?
输入
第一行包含一个整数。nn (2≤n≤3⋅1052≤n≤3⋅105)-树中的顶点数。
第二行包含nn整数a1,a2,…,ana1,a2,…,an (0≤ai≤20≤ai≤2)-顶点的颜色。ai=1ai=1意味着那个顶点ii是红色的,ai=2ai=2意味着那个顶点ii是蓝色的ai=0ai=0意味着那个顶点ii是没有颜色的。
这个ii-下一个n−1n−1行包含两个整数vivi和uiui (1≤vi,ui≤n1≤vi,ui≤n, vi≠uivi≠ui)-树的边缘。保证给定的边形成一棵树。保证树包含至少一个红色顶点和至少一个蓝色顶点。
输出量
打印一个整数-好的,漂亮的给定树的边缘。
实例
输入
复制
5 2 0 0 1 2 1 2 2 3 2 4 2 5输出量
复制
1输入
复制
5 1 0 0 0 2 1 2 2 3 3 4 4 5输出量
复制
4输入
复制
3 1 1 2 2 3 1 3输出量
复制
0注
下面是第一个示例中的树:
唯一好的,漂亮的边是边(2,4)(2,4)..移除它会使树分崩离析。{4}{4}和{1,2,3,5}{1,2,3,5}..第一分量只包括红色顶点,第二分量包括蓝色顶点和未着色顶点。
下面是第二个示例中的树:
每一条边都是好的,漂亮的在里面。
下面是第三个例子中的树:
边缘(1,3)(1,3)将其分解为组件{1}{1}和{3,2}{3,2},后者既包括红色顶点,也包括蓝色顶点,因此边不是好的,漂亮的..边缘(2,3)(2,3)将其分解为组件{1,3}{1,3}和{2}{2},前者包括红色和蓝色顶点,因此边也不是好的,漂亮的..答案是0。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
ll r,b,n;
struct node{
ll r,b;//红色,蓝色
}num[300001];
vector<ll> p[300001];
void dfs(ll u,ll fa){
ll len=p[u].size();
for(ll i=0;i<len;i++){
ll v=p[u][i];
if(v==fa)continue;
dfs(v,u);
//合并颜色
num[u].r+=num[v].r;
num[u].b+=num[v].b;
}
}
ll ans;
void dfs_plus(ll u,ll fa){
ll len=p[u].size();
for(ll i=0;i<len;i++){
ll v=p[u][i];
if(v==fa){
continue;
}
if(num[v].b==b&&num[v].r==0||num[v].r==r&&num[v].b==0){
ans++;
}
dfs_plus(v,u);
}
}
int main(){
ll pic;
cin>>n;
for(ll i=1;i<=n;i++){
cin>>pic;
if(pic==1){
r++;
num[i].r++;
}
else if(pic==2){
b++;
num[i].b++;
}
}
ll x,y;
for(ll i=1;i<n;i++){
cin>>x>>y;
p[x].push_back(y);
p[y].push_back(x);
}
ans=0;
dfs(1,0);
dfs_plus(1,0);
cout<<ans<<endl;
return 0;
}