Codeforces Round 496 (Div. 3) F. Berland and the Shortest Paths

题目意思:有n个城市和m条道路数量,其中m > n - 1。从编号为 1的城市出发,可以沿着道路到达任何其他城市。选择n - 1条道路,使得1到所有城市的距离最短。如果选择方案数少于k,输出所有方案,否则输出k种方案。
这题可以看成一个以1为起点的bfs树,每个节点v需要从所有能作为其父节点的边里面选则一条,也就是dis[u] + 1 = dis[v]。不同的选择组合就是不同的答案。
由此我们先建图跑bfs,求每一个节点到1的最小距离,然后将所有能作为这个点父节点的点放入一个集合里面。

预处理代码
int n, m, k;
cin >> n >> m >> k;
vector <vector <int>> adj(n + 1);
vector <pll> edges(m + 1);
for(int i = 1; i <= m; i++){
	int u, v;
	cin >> u >> v;
	//用于查找可能父节点
	edges[i] = {u, v};
	//建图用于跑bfs
	adj[u].push_back(v);
	adj[v].push_back(u);
}
BFS
//求每一个级节点到1的最短距离
vector <int> dis(n + 1, INT_MAX);
dis[1] = 0;//初始化自身
queue <int> q;
q.push(1);

//BFS
while(!q.empty()){
	int u = q.front();
	q.pop();
	//遍历u的子节点
	//由于bfs特性,每次扩散到的一定是最短的
	for(auto v : adj[u]){
		if(dis[v] == INT_MAX) {
			dis[v] = dis[u] + 1;
			q.push(v);
		}
	}
}

在得到每个点关于1的最短距离之后,我们现在可以将所有点和父节点相连的边的编号放入一个集合里面,方便我们选择。

加入集合
vector <vector <int>> fa(n + 1);
vector <vector <int>> fa(n + 1);
for(int i = 1; i <= m; i++){
	int u = edges[i].first, v = edges[i].second;
	//如果dis[u] = dis[v] + 1,说明v是u的父节点
	//反之,如果dis[v] = dis[u] + 1,说明u是v的父节点
	//将边的编号推入dis
	if(dis[u] == dis[v] + 1) fa[u].push_back(i);
	if(dis[v] == dis[u] + 1) fa[v].push_back(i);
}
	

接下来是从所有可选的方式中选出k种,如果不足k种则选择所有。我们考虑枚举所有可能的组合,并将前k种存入结果数组ans里。

枚举
vector <int> idx (n + 1, 0); //当前第i个用了多少父节点
while(ans.size() < k){
	string s(m, '0');
	for(int v = 2; v <= n; v++){
		int ch = fa[v][idx[v]];  //对每个城市,从fa[v]选出第idx[v]个父边ch
		s[ch - 1] = '1'; //将父边标记为'1';
	}
	ans.push_back(s); //保存当前结果
	
	bool have_use = false;
	for(int v = n; v >= 2; v--){
		if(idx[v] + 1 < fa[v].size()){
			idx[v]++;
			have_use = true; // 只要有一个没有枚举完,就不会停止
			break;
		} else idx[v] = 0;
	}
	if(!have_use) break;
}

最后输出结果

输出
cout << ans.size() << endl;
for(string t : ans) cout << t << endl;

AC代码

#include <bits/stdc++.h>
#define i64 long long
#define ui64 unsigned long long
#define pll pair<int, int>
using namespace std;

void solve(){
	int n, m, k;
	cin >> n >> m >> k;
	vector <vector <int>> adj(n + 1);
	vector <pll> edges(m + 1);
	for(int i = 1; i <= m; i++){
		int u, v;
		cin >> u >> v;
		//用于查找可能父节点
		edges[i] = {u, v};
		//建图用于跑bfs
		adj[u].push_back(v);
		adj[v].push_back(u);
	}
	
	//求每一个级节点到1的最短距离
	vector <int> dis(n + 1, INT_MAX);
	dis[1] = 0;//初始化自身
	queue <int> q;
	q.push(1);
	
	//BFS
	while(!q.empty()){
		int u = q.front();
		q.pop();
		//遍历u的子节点
		//由于bfs特性,每次扩散到的一定是最短的
		for(auto v : adj[u]){
			if(dis[v] == INT_MAX) {
				dis[v] = dis[u] + 1;
				q.push(v);
			}
		}
	}
	
	vector <vector <int>> fa(n + 1);
	for(int i = 1; i <= m; i++){
		int u = edges[i].first, v = edges[i].second;
		//如果dis[u] = dis[v] + 1,说明v是u的父节点
		//反之,如果dis[v] = dis[u] + 1,说明u是v的父节点
		//将边的编号推入dis
		if(dis[u] == dis[v] + 1) fa[u].push_back(i);
		if(dis[v] == dis[u] + 1) fa[v].push_back(i);
	}
		
	vector <string> ans;
	vector <int> idx (n + 1, 0); //当前第i个用了多少父节点
	while(ans.size() < k){
		string s(m, '0');
		for(int v = 2; v <= n; v++){
			int ch = fa[v][idx[v]];  //对每个城市,从fa[v]选出第idx[v]个父边ch
			s[ch - 1] = '1'; //将父边标记为'1';
		}
		ans.push_back(s); //保存当前结果
		
		bool have_use = false;
		for(int v = n; v >= 2; v--){
			if(idx[v] + 1 < fa[v].size()){
				idx[v]++;
				have_use = true; // 只要有一个没有枚举完,就不会停止
				break;
			} else idx[v] = 0;
		}
		if(!have_use) break;
	}
	cout << ans.size() << endl;
	for(string s : ans) cout << s << endl;

}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
	
	int t = 1;
	//cin >> t;
    while(t--)solve();
    return 0;
}
posted @ 2025-10-19 00:35  扶风zer0  阅读(7)  评论(0)    收藏  举报