P5663 加工零件
题解
暴力搜索
搜索显然会TLE
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<string> #include<cstring> #include<queue> using namespace std; typedef long long ll; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } const int maxn=1e5+10; struct node{ int to,nxt; }edge[maxn*2]; int head[maxn],cnt=0; int n,m,q; bool vis[maxn];//节点i是否与1直接相连 int son[1005][1005]; int num[1005]; void addedge(int u,int v) { edge[++cnt].to =v;edge[cnt].nxt =head[u];head[u]=cnt; edge[++cnt].to =u;edge[cnt].nxt =head[v];head[v]=cnt; } void dfs(int sum,int u) { if(sum<0) return; if(num[1]==0) return; for(int v,i=1;i<=son[u][0];i++){ v=son[u][i]; num[v]=min(num[v],sum); dfs(sum-1,v); } } int main() { n=read();m=read();q=read(); vis[1]=1; for(int u,v,i=1;i<=m;i++){ u=read();v=read(); if(u==1) vis[v]=1; if(v==1) vis[u]=1; son[u][++son[u][0]]=v; son[v][++son[v][0]]=u; addedge(u,v); addedge(v,u); } for(int a,l,t=1;t<=q;t++){ memset(num,63,sizeof(num)); a=read();l=read(); if(l==1){ if(a==1||(!vis[a])) { printf("No "); continue; } else if(vis[a]&&a!=1) { printf("Yes "); continue; } }else{ num[a]=l; dfs(l-1,a); if(num[1]==0) printf("Yes "); else printf("No "); } } return 0; }
考虑正解 SPFA最短路
题目给出一张无向图,他大概长这个亚子
对于给出一个点 a 要完成第 L 阶段任务,也就是从 a 到 1 找一条长度为 L 的路径
我们发现
1. 点2到1的距离为1(奇数),如果2完成奇数阶段任务,那么1就要为它提供原料,如果是偶数阶段任务就不用
2. 点5到点1的距离为2(偶数),如果5完成偶数阶段任务,那么1就要为它提供原料,如果是奇数阶段任务就不用
如果2到1的距离是5,也就是大于他们之间最短距离,那么要完成的阶段就会在二者之间不断传递(2-->5 , 1-->4 , 2-->3 , 1-->2 , ....),最后会变成上面分析的情况1
也就是说
1. 如果点a要完成的任务阶段数大于等于他们之间的最短距离,都可以化简成处理他们之间的最短距离来做
2. 如果a要完成的任务阶段数本就小于他们之间的最短距离,也就是a在扩展到1之前就已经有别的点为它提供原料,1节点自然不用提供原料
我们现在就要考虑上面说的第一种情况 L >= dis(a-->1)
由于从一个节点出发到达1会有很多路径,我们用 dis[ i ][ 0 ] 记录从节点 i 到节点 1 的长度为偶数的最短路的长度,用 dis[ i ][ 1 ] 记录从节点 i 到节点 1 的长度为奇数最短路的长度,SPFA 具体实现
更新数组的柿子也就是:
dis[ v ][ 0 ] = min ( dis[ v ][ 0 ] , dis[ u ][ 1 ] + 1 )
dis[ v ][ 1 ] = min ( dis[ v ][ 1 ] , dis[ u ][ 0 ] + 1 )
综上分析得出结论
1. 点 a 要完成的阶段任务数为偶数, 如果从a-->1存在一条偶数最短路,并且它的长度小于等于该任务数,1需要提供原料
2. 点 a 要完成的阶段任务数为奇数, 如果从a-->1存在一条奇数最短路,并且它的长度小于等于该任务数,1需要提供原料
3. 其余情况就不用1提供原料了
代码
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<string> #include<cstring> #include<queue> using namespace std; typedef long long ll; int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } const int maxn=1e5+10; int n,m,qus; struct node{ int to,nxt; }edge[maxn<<1]; int cnt=0,head[maxn]; inline void addedge(int u,int v) { edge[++cnt].to =v,edge[cnt].nxt =head[u],head[u]=cnt; edge[++cnt].to =u,edge[cnt].nxt =head[v],head[v]=cnt; } int dis[maxn][2]; bool vis[maxn]; inline void spfa() { queue<int>q; memset(dis,63,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[1][0]=0; vis[1]=1; q.push(1); while(!q.empty() ){ int u=q.front() ; q.pop() ; vis[u]=0; for(int v,i=head[u];i;i=edge[i].nxt ){ v=edge[i].to ; if(dis[v][1]>dis[u][0]+1){ dis[v][1]=dis[u][0]+1; if(!vis[v]){ q.push(v); vis[v]=1; } } if(dis[v][0]>dis[u][1]+1){ dis[v][0]=dis[u][1]+1; if(!vis[v]){ q.push(v); vis[v]=1; } } } } } int main() { n=read();m=read();qus=read(); for(int u,v,i=1;i<=m;i++){ u=read();v=read(); addedge(u,v); } spfa(); for(int a,l,i=1;i<=qus;i++){ a=read();l=read(); if((l%2)&&(dis[a][1]<=l))printf("Yes "); else if((l%2==0)&&(dis[a][0]<=l))printf("Yes "); else printf("No "); } return 0; }