CF507E Breaking Good题解
题目传送门
题意分析:
这题最重要的是要看懂题目意思:
- 给出 n 个城市和 m 条边,其中 z 表示此边是否能通行。
- 求一条从城市
到 n 尽可能短且对道路的影响(即炸毁或修复)尽可能小的路。
那么如何使选出的路对道路的影响小呢?
多读几遍题可知:
- 对于在所选路径上的路,所要修复(即不可通行)的路要尽可能少。
- 对于非所选路径上的路,所要炸毁(即可通行)的路要尽可能少。
解:
这题用最短路做(关于 SPFA ,它死了。),于是我用 Dijkstra 做。
根据刚才的分析,
我们用
更新时考虑找路径最短,且路径中不可通行路数量少:
//it.nxt表示下一个城市,now表示现在的城市,
//it.w表示这条路能否通行(因为 0 表示不可通行,所以加 !it.w,即加 1)。
if((dis[it.nxt]==dis[now]+1&&path[it.nxt]>path[now]+!it.w)||dis[it.nxt]>dis[now]+1)
{
更新...
}
输出:
这题的输出也是有点麻烦。
我们先用
跑完 Dijkstra 后从 n 点递归标记选出的最短路:
void Print(int x)
{
vis[x]=1;
if(pre[x])
Print(pre[x]);
}
int main()
{
...
memset(vis,0,sizeof(vis));
Print(n);
...
}
然后对于所有边按题意模拟一遍输出即可。(具体见代码)
Code:
注:
本代码中有关 auto 的用法最好用 C++17 提交,
不过其实这个在 C++14 就能用(只是会警告,我平时都用 C++14 交的),
但这题用 C++14 在 CF 上交会 CE。
#include<bits/stdc++.h>
using namespace std;
struct Node{
int nxt;bool w;
};
int n,m;
vector<Node>a[100001];//本人较懒,就用vector存边了
int dis[100001],vis[100001],path[100001],pre[100001];
typedef pair<int,int> Pii;
priority_queue<Pii,vector<Pii>,greater<Pii> >q;//小根堆
void Dijkstra(int s)//堆优化 Dijkstra
{
memset(dis,0x3f,sizeof(dis));//初始化
memset(path,0x3f,sizeof(path));//别忘记初始化 path
dis[s]=0;path[s]=0;q.emplace(0,s);
while(!q.empty())
{
auto [w,now]=q.top();q.pop();
if(vis[now])continue;
vis[now]=1;
for(auto it:a[now])//遍历与城市 now 相连的城市,用 auto 可以省很多码。
{
if((dis[it.nxt]==dis[now]+1&&path[it.nxt]>path[now]+!it.w)||dis[it.nxt]>dis[now]+1)
{
//更新
dis[it.nxt]=dis[now]+1;
path[it.nxt]=path[now]+!it.w;
pre[it.nxt]=now;
q.emplace(dis[it.nxt],it.nxt);
}
}
}
}
void Print(int x)//递归标记选出的路径
{
vis[x]=1;
if(pre[x])
Print(pre[x]);
}
int ans[100001][3],sum;//用来存答案
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,u,v,w;i<=m;i++)//无向图存边
cin>>u>>v>>w,a[u].emplace_back((Node){v,w}),a[v].emplace_back((Node){u,w});
Dijkstra(1);
memset(vis,0,sizeof(vis));
Print(n);
for(int i=1;i<=n;i++)
{
for(auto it:a[i])
{
if((vis[i]&&vis[it.nxt]&&it.w==1)||(!(vis[i]&&vis[it.nxt])&&it.w==0))continue;//按题意模拟
else if(i<it.nxt)//无向图防止重复输出
{
sum++;
ans[++ans[0][0]][0]=i,ans[ans[0][0]][1]=it.nxt,ans[ans[0][0]][2]=!it.w;//记录答案,修复和炸毁和原先反一下,所以存 !it.w
}
}
}
cout<<sum<<'\n';//输出
for(int i=1;i<=ans[0][0];i++)
cout<<ans[i][0]<<' '<<ans[i][1]<<' '<<ans[i][2]<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话