加工零件
加工零件
题目简述:
这道题描述得还是很详尽的,两个样例也都有解释,良心啊(赞qwq)
这里就不再多赘述了,直接来讲我的解题过程ovo
暴力起手
读完样例解释,知道是图论但并不是很懂为什么会用最短路,也没有其他什么思路,于是...来看看数据范围
测试点1~4:1≤n,m≤1000,q=3,L=1
测试点5~8:1≤n,m≤1000,q=3,1≤L≤10
- 暴力起手
这八个点好小,岂不是送分?那就愉快地暴力起手吧!(其实就是没思路)
怎么暴力呢?我们用递归模拟来解决:
-
面对每组的Ai、Li,我们先判断是否都等于1,如果是直接输出“No”(其实不用这步)
-
如果不是,就清零存答案的ans再进入递归处理
-
我们通过链式前向星遍历与Ai相连的所有边:设与Ai相连的点是Vj,我们就将Vj作为新的Ai进入下一层递归,当然,这时Li也应该同时减一
很简单对吧?然后我们将代码实现出来,就能够拿到40pts了,如下:
#include <bits/stdc++.h>
using namespace std;
int n,m,q,u,v,a,l,tot,ans;
int head[100010];
struct node {
int to,net;
} e[100010];
inline void add(int u,int v) {
e[++tot].to=v;
e[tot].net=head[u];
head[u]=tot;
}
inline void solve(int x,int y) {
if(ans==1) return ; //已经有答案就退出,剪枝+5pts
if(y==0) {
if(x==1) ans=1; //如果1号需要提供原材料,标记ans
return;
}
for(register int i=head[x];i;i=e[i].net) {
int v=e[i].to;
solve(v,y-1); //进入下一层递归
}
}
int main() {
scanf("%d%d%d",&n,&m,&q);
for(register int i=1;i<=m;i++) {
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(register int i=1;i<=q;i++) {
scanf("%d%d",&a,&l);
if(a==1&&l==1) puts("No");
else {
ans=0; //清零ans
solve(a,l);
if(ans==1) puts("Yes");
else puts("No");
}
}
return 0;
}
- 暴力优化
可以发现,Ai、Li在递归中会出现很多状态的重复计算,那么我们就可以使用记忆化来剪枝啊!这样我们可以拿到60pts!
具体实现:用一个数组记录每次递归遇到的新的Ai、Li,下一次遇到相同的就直接输出记录的答案
PS:记忆化代码就不给出了,很简单就能够实现,不多赘述
漫漫正解之路:
再确认暴力程序已经不可优化后,我们来思考一下能拿更多分的做法
所以,来研究样例:
-
1->2的最短奇数路径长度为1,所以当2的Li为奇数且不小于1时,1总是要提供原材料
-
1->2的最短偶数路径长度为2,所以当2的Li为偶数且不小于2时,1总是要提供原材料
-
1->2->3的最短偶数路径长度为2,所以当3的Li为偶数且不小于2时,1总是要提供原材料
-
1->2->3的最短奇数路径长度为3,所以当3的Li为奇数且不小于3时,1总是要提供原材料
综上,我们可以将题意转换一下:
求1到Ai的最短奇数路径长度和最短偶数路径长度
若Ai的Li为偶数,且存在1到Ai的最短偶数路径长度dis[Ai][0],满足Li≥dis[Ai][0],那么输出“Yes”
若Ai的Li为奇数,且存在1到Ai的最短奇数路径长度dis[Ai][1],满足Li≥dis[Ai][1],那么也输出“Yes”
那么我们就得到了另外一种思路,并且和最短路联系上了!
但是还有几个细节需要注意(我用Dijkstra实现最短路):
-
这道题的Dijkstra部分不用vis数组来记录当前点是否入过队
-
通过上面的递归做法我们能够知道一个点是需要通过多次的,但vis标记后一个点就只能走一次,明显与题意相违背,所以我们去掉vis数组
-
我们提交后会发现没有得到100pts,怎么回事?再想一下,1可能是孤岛!什么意思?就是1没与任何其他的点相连,所以我们特判一下即可
现在给出满分code:
#include <bits/stdc++.h>
using namespace std;
int n,m,q,u,v,a,l,tot,ans,flag;
int dis[200010][2],head[200010];
queue<pair<int,int> > shan;
struct node {
int to,net;
} e[200010];
inline void add(int u,int v) {
e[++tot].to=v;
e[tot].net=head[u];
head[u]=tot;
}
inline void dijkstra() { //在普通模板上分成奇偶处理
memset(dis,127,sizeof(dis));
dis[1][0]=0; //将1的偶数路径赋值为0,奇数路径设为较大值
shan.push(make_pair(0,1)); //入队
while(!shan.empty()) {
int x=shan.front().second;
shan.pop();
for(register int i=head[x];i;i=e[i].net) {
int v=e[i].to;
if(dis[v][0]>dis[x][1]+1) { //用奇数+1更新偶数
dis[v][0]=dis[x][1]+1;
shan.push(make_pair(dis[v][0],v));
}
if(dis[v][1]>dis[x][0]+1) { //用偶数+1更新奇数
dis[v][1]=dis[x][0]+1;
shan.push(make_pair(dis[v][1],v));
}
}
}
}
int main() {
scanf("%d%d%d",&n,&m,&q);
for(register int i=1;i<=m;i++) {
scanf("%d%d",&u,&v);
// if(u==1||v==1) flag=1;
add(u,v);
add(v,u);
}
dijkstra();
for(register int i=1;i<=q;i++) {
scanf("%d%d",&a,&l);
// if(flag==0) puts("No");
if(!head[1]) puts("No"); //也可以引入flag来标记1是否是孤岛
else if(l%2==0&&dis[a][0]<=l) puts("Yes"); //判断偶数最短路是否可行
else if(l%2==1&&dis[a][1]<=l) puts("Yes"); //判断奇数最短路是否可行
else puts("No");
}
return 0;
}
最后,如果有任何不懂的地方或有任何我的理解不对的地方,欢迎大家留言啊qvq
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】