AtCoder Beginner Contest 309 - D(最短路) E(搜索?)
题目传送门:abc 309
前面的简单题就不放了
D - Add One Edge
题意:
给你一个无向图图,分为两个连通块,一个顶点数为 n1(1~n1),一个顶点数为 n2(n1+1~n1+n2),图中共有 m 条边。如果现在在两个连通块之间连接一条边,那么顶点 1 与顶点 n1+n2 则相互可达,且对于此种连法,两顶点之间存在一条最短的可达路径。问你在所有的情况中,最短路径的最大值是多少?
思路:
法一:dijkstra
因为问的是最短距离的最大,所以我们首先需要每一个连通块内两顶点到所有点的最短距离,又需要最大的最短距离,所以两个连通块内的最大距离之和+1即为最终答案。
首先利用 dij 处理顶点 1 和顶点 n1+n2 的单源最短路,设顶点 1 所在的连通块与顶点 1 的最远距离为 ans1,顶点 n1+n2 所在的连通块与顶点 n1+n2 的最远距离为ans2,最终的答案即为\(1+ans_1+ans_2\)
注意一点,就是\(0\le n_1+n_2\le 3e5\),注意全局变量的空间大小!
const int maxm=3e5+5,inf=0x3f3f3f3f,mod=998244353;
int n1,n2,m,ans[2];
vector<int> e[maxm],dis(maxm,inf);
vector<int> vis(maxm,false);
void dij(int x,int p){
priority_queue<pii,vector<pii>,greater<pii>> q;
q.push({0,x});
pii t;
while(!q.empty()){
t=q.top();
q.pop();
if(vis[t.second]) continue;
dis[t.second]=t.first;
ans[p]=max(ans[p],t.first);
vis[t.second]=true;
for(auto a:e[t.second]){
if(!vis[a] && dis[a]>t.first+1){
q.push({t.first+1,a});
}
}
}
return ;
}
void solve(){
cin>>n1>>n2>>m;
int a,b;
for(int i=0;i<m;++i){
cin>>a>>b;
e[a].push_back(b);
e[b].push_back(a);
}
dij(1,0);
dij(n1+n2,1);
cout<<ans[0]+ans[1]+1<<'\n';
return ;
}
法二:BFS+队列
其实bfs基本思想与dij相同,利用BFS+队列逐层遍历
const int maxm=3e5+5,inf=0x3f3f3f3f,mod=998244353;
int n1,n2,m,ans[2];
vector<int> e[maxm],dis(maxm,inf);
vector<bool> vis(maxm,false);
void bfs(int x,int p){
dis[x]=0;
queue<int> q;
q.push(x);
int t;
while(!q.empty()){
t=q.front();
q.pop();
vis[t]=true;
for(auto a:e[t]){
if(!vis[a] && dis[a]>dis[t]+1){
dis[a]=dis[t]+1;
ans[p]=max(ans[p],dis[t]+1);
q.push(a);
}
}
}
return ;
}
void solve(){
cin>>n1>>n2>>m;
int a,b;
for(int i=0;i<m;++i){
cin>>a>>b;
e[a].push_back(b);
e[b].push_back(a);
}
bfs(1,0);
bfs(n1+n2,1);
cout<<ans[0]+ans[1]+1<<'\n';
return ;
}
E - Family and Insurance
题意:
给你一个家族的父子关系,再给你部分人购买的家族保险的所能保的代数,问你这个家族共有多少人能够被保险保到?
思路:
简单题,但是自己犯了两个错误。。。
首先,父子关系有个限制\(1\le p_i\le i-1\),这说明序号大的一定是某个序号小的的子代(在Constraints中没看到啊没看到!)
第二点,全局变量默认初始化为0,但是非全局变量默认值又系统决定,所以,在使用需定义初值的非全局化变量之前,记得初始化啊!!!不然就会WA
由上我们可以对所有人存一个到自己的保险还能管几代-dp,在输入保险时不断更新单独某个人的最大值。之后因为父子关系是从前往后的,相当于我们已经知道题目的拓扑序了,所以我们从第2个人开始更新自己的保险管辖范围,除了自己的保险还有将父辈的保险继承下来,dp[i]=max(dp[i],dp[p[i]]-1)
。最终,统计有多少人有保险覆盖即可(初值可以赋-1方便判断)
所以,题目给的是一个有权值的有根树,问权值向下传递,能覆盖多少节点
下为代码:
//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
*/
const int maxm=3e5+5,inf=0x3f3f3f3f,mod=998244353;
void solve(){
int n,m,ans=0;
cin>>n>>m;
vector<int> p(n+1,0);
for(int i=2;i<=n;++i){
cin>>p[i];
}
vector<int> dp(n+1,-1);
for(int i=0;i<m;++i){
int x,y;
cin>>x>>y;
dp[x]=max(dp[x],y);
}
for(int i=2;i<=n;++i){
dp[i]=max(dp[i],dp[p[i]]-1);
}
for(int i=1;i<=n;++i){
if(dp[i]>=0) ++ans;
}
cout<<ans<<'\n';
return ;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
也可以和上题一样,利用bfs+队列求解问题。这么说的话,dfs也可以。
下为bfs代码:
void bfs(int x){
queue<int> q;
q.push(x);
while(!q.empty()){
int t=q.front();
q.pop();
if(dp[t]>=0) ++ans;
for(auto a:e[t]){//e[t]中存了t的直接子代的编号
dp[a]=max(dp[a],dp[t]-1);
q.push(a);
}
}
return ;
}
本文来自博客园,作者:Qiansui,转载请注明原文链接:https://www.cnblogs.com/Qiansui/p/17553733.html