广搜优化题目总结
电路维修*
这道题之前打过,但那时候打题太水了,没有真正掌握这道题的知识点。(果然我还是太蒻了)
这道题的解法是先建边,对于每一个单位正方形,将有边相连的两个对角建一条长度为0的无向边,另外两个对角建一条长度为1的无向边。然后可以跑最短路或者用双端队列bfs(0-1bfs)。
跑最短路的话要注意由于是网格图spfa会炸掉,所以要用堆优化的dijkstra( ~~不会不加堆优化的dijkstra (^_^)~~ )
因为在练习广搜,所以我用的是双端队列bfs
当我们遇到边权只有1的情况时,显然直接bfs,然后第一次访问到某个点的“时间”就是到它的最短路径
当边权同时存在1和0两种情况时,我们就可以采用双端队列bfs,也叫作0-1bfs,就是访问时,若边权为0,则把目标点v放在队首,若边权为1,则把目标点放在队尾,这样是为了使得边权为0时的目标点v先被访问。
我按照这个思路做了,但却WA了,后来我一看题解,队列里维护的居然不是点,而是边,我一直想不通为什么,找了半天也只有题解模糊地写到,然后我就自己手玩了一把,发现如果是维护点,那么有可能会出现这样的情况:显然如果边权为0,那么目标点v与点u应该处于广搜的同一层,但如果是维护点,就可能会出现点v在被点u访问到之前被下一层的点提前访问了(也就是由这一层的点通过边权为1的边得到的目标点vv访问过来(因为如果是维护点,那么就会像是一个深搜的过程,会在我把这一层访问完之前,下一层的点先把这一层的点访问了))。但如果我们是维护边的话,那么将会起到一个滞留的作用,能够留给我时间,让我先把这一层的访问完了,在访问下一层。
这道题我还有一个收获就是知道了链式前向星还能储存入边,以前我的不知道来着,,,
这是我WA掉的访问点的代码(注释掉的是我手玩搜索过程的代码)
#include <iostream> #include <queue> #include <deque> #include <cstdio> #include <cstring> using namespace std; #define Maxn 280000 int n,m; deque<int>q; int fir[Maxn],nxt[Maxn*2],vv[Maxn*2],edg[Maxn*2]; int tot=0,ans=0; int vis[Maxn],dis[Maxn]; void add(int u,int v,int w) { nxt[++tot]=fir[u]; fir[u]=tot; vv[tot]=v; edg[tot]=w; } //int room[1000]; int id(int x,int y){return (x-1)*(m+1)+y;} void bfs() { memset(dis,-1,sizeof(dis)); q.push_front(id(1,1));vis[id(1,1)]=1;dis[id(1,1)]=0; while(!q.empty()) { // int cnt=0; // printf("q:"); // while(!q.empty()) // { // room[++cnt]=q.front();q.pop_front(); // printf("%d ",room[cnt]); // } // printf("\n"); // for(int i=cnt;i>=1;i--)q.push_front(room[i]); int u=q.front();q.pop_front(); // printf("u=%d dis[u]=%d\n",u,dis[u]); for(int i=fir[u];i;i=nxt[i]) { // printf("u->v: %d->%d\n",u,vv[i]); int v=vv[i]; if(vis[v]==1)continue; vis[v]=1; dis[v]=dis[u]+edg[i]; if(v==id(n+1,m+1))return; if(edg[i]==0)q.push_front(v); else q.push_back(v); } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { string str;cin>>str; for(int j=1;j<=m;j++) { int x1=id(i,j),x2=id(i+1,j+1),y1=id(i+1,j),y2=id(i,j+1); if(str[j-1]=='/') { add(x1,x2,1);add(x2,x1,1); add(y1,y2,0);add(y2,y1,0); } else { add(x1,x2,0);add(x2,x1,0); add(y1,y2,1);add(y2,y1,1); } } } bfs(); /* for(int i=1;i<=n+1;i++) for(int j=1;j<=m+1;j++) printf("id(%d,%d)=%d\n",i,j,id(i,j)); for(int i=1;i<=id(n+1,m+1);i++) printf("dis[%d]=%d\n",i,dis[i]); printf("id(n+1,m+1)=%d\n",id(n+1,m+1));printf("dis[%d]=%d\n",id(n+1,m+1),dis[id(n+1,m+1)]);*/ if(dis[id(n+1,m+1)]==-1)printf("NO SOLUTION\n"); else printf("%d\n",dis[id(n+1,m+1)]); return 0; }
这是正解
#include <iostream> #include <queue> #include <deque> #include <cstdio> #include <cstring> using namespace std; #define Maxn 2800000 int n,m; deque<int>q; int fir[Maxn],nxt[Maxn*4],uu[Maxn*4],vv[Maxn*4],edg[Maxn*4]; int tot=0,ans=0; int vis[Maxn],dis[Maxn]; void add(int u,int v,int w) { nxt[++tot]=fir[u]; fir[u]=tot; uu[tot]=u; vv[tot]=v; edg[tot]=w; } int id(int x,int y){return (x-1)*(m+1)+y;} void bfs() { memset(dis,-1,sizeof(dis)); memset(vis,0,sizeof(vis)); q.clear(); q.push_front(id(1,1));vis[id(1,1)]=1;dis[id(1,1)]=0; while(!q.empty()) { int e=q.front();q.pop_front(); int u=uu[e];int v=vv[e]; if(vis[v]==1)continue; vis[v]=1; dis[v]=dis[u]+edg[e]; if(v==id(n+1,m+1))return; for(int i=fir[v];i;i=nxt[i]) { if(vis[vv[i]]==1)continue; if(edg[i]==0)q.push_front(i); else q.push_back(i); } } } int main() { int T;scanf("%d",&T); while(T--) { tot=0;memset(fir,0,sizeof(fir)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { string str;cin>>str; for(int j=1;j<=m;j++) { int x1=id(i,j),x2=id(i+1,j+1),y1=id(i+1,j),y2=id(i,j+1); if(str[j-1]=='/') { add(x1,x2,1);add(x2,x1,1); add(y1,y2,0);add(y2,y1,0); } else { add(x1,x2,0);add(x2,x1,0); add(y1,y2,1);add(y2,y1,1); } } } bfs(); if(dis[id(n+1,m+1)]==-1)printf("NO SOLUTION\n"); else printf("%d\n",dis[id(n+1,m+1)]); } return 0; }
未完待续...