首先,这篇博客主要是写给自己看的所以并没有很具体。另外,为了避免误人子弟,先声明一下第一种写法其实是不太合理的,第二种请往下看。
因为没有边权,所以广搜+哈希判重(以减少不必要的搜索)。
用状态压缩的方式存储钥匙的状态。
因为哈希哈希得不够好T了,95分。
哈希的时候要注意分析到底哪一些信息是有用的,哪一些信息是没有用的,从而将更多冗余的点压缩在一起,从而降低运行时间。
具体地说
1.其实对于每个node来说,step是无关紧要的,因为只要是在同一个位置并且当前的钥匙相同,那么就是同一个状态,并且step小的一定会先搜到,之后就通通丢掉不要。
2.并不需要deleteHash,理由同上。
另:哈希的时候内存够哈希表尽量越大越好。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(r);i>=(l);i--)
using namespace std;
const int N=6e3,M=7e3,inf=1e9,base=13131,mod=16777215;
struct node{
int nowkey,step,pos;
};
int n,m,k,roomkey[N],a,ans=inf;
int nxt[M],to[M],head[N],roadkey[M],len;
int times[N];
node myh[mod]; int cnt[mod];
queue<node> q;
void addedge(int u,int v,int rk){ nxt[++len]=head[u]; to[len]=v; head[u]=len; roadkey[len]=rk; }
bool equal(node a,node b){
if(((a.nowkey==b.nowkey))&&(a.pos==b.pos)) return true;
return false;
}
void myhash(node a){
int pos=(a.nowkey*base+a.pos*base*base)&mod;
while((cnt[pos]>0)&&(equal(myh[pos],a)==false)) pos=(pos+1)&mod;
cnt[pos]++; myh[pos]=a;
}
bool findhash(node a){
int pos=(a.nowkey*base+a.pos*base*base)&mod;
while((cnt[pos]>0)){
if(equal(myh[pos],a)==true) return true;
pos=(pos+1)&mod;
}
return false;
}
/*void deletehash(node a){
int pos=(a.nowkey*base+a.pos*base*base+a.step*base*base*base)&mod;
while((cnt[pos]>0)&&(equal(myh[pos],a)==false)) pos=(pos+1)&mod;
cnt[pos]--;
}*/
int main(){
scanf("%d%d%d",&n,&m,&k);
rep(i,1,n) rep(j,0,k-1) {//从最低位用起
scanf("%d",&a);
roomkey[i]|=(a<<j);
}
rep(i,1,m){
int x,y,rk=0;
scanf("%d%d",&x,&y);
rep(j,0,k-1){
scanf("%d",&a);
rk|=(a<<j);
}
addedge(x,y,rk);
}
node fir; fir.nowkey=roomkey[1]; fir.step=0; fir.pos=1; q.push(fir);
while(!q.empty()){
node hd=q.front(); q.pop();// deletehash(hd);
//printf("pos=%d\n",hd.pos);
if(hd.pos==n){
ans=hd.step;
break;
}
if(hd.step>30000) break;
for(int i=head[hd.pos];i!=0;i=nxt[i]){
if((hd.nowkey&roadkey[i])==roadkey[i]){
node tmp;
tmp.nowkey=(hd.nowkey|roomkey[to[i]]);
tmp.pos=to[i];
tmp.step=hd.step+1;
if(findhash(tmp)) continue;
q.push(tmp); myhash(tmp);
}
}
}
if(ans==inf) printf("No Solution"); else printf("%d",ans);
return 0;
}
UPD:其实并不需要用哈希表。。因为房间最多只有5000个,而每个房间能有的钥匙状态只有2^10,即1024种,开一个[5000][1024]的bool数组来存储当前某个状态是否到达过了就可以实现和哈希一样的功能了。不仅代码量大大下降,而且时间复杂度更低。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(r);i>=(l);i--)
using namespace std;
const int N=6e3,M=7e3,inf=1e9,base=13131,mod=16777215;
struct node{int nowkey,step,pos;};
int n,m,k,roomkey[N],a,ans=inf;
int nxt[M],to[M],head[N],roadkey[M],len;
bool ex[N][1111];
queue<node> q;
void addedge(int u,int v,int rk){ nxt[++len]=head[u]; to[len]=v; head[u]=len; roadkey[len]=rk; }
int main(){
scanf("%d%d%d",&n,&m,&k);
rep(i,1,n) rep(j,0,k-1) {//从最低位用起
scanf("%d",&a);
roomkey[i]|=(a<<j);
}
rep(i,1,m){
int x,y,rk=0;
scanf("%d%d",&x,&y);
rep(j,0,k-1){
scanf("%d",&a);
rk|=(a<<j);
}
addedge(x,y,rk);
}
node fir; fir.nowkey=roomkey[1]; fir.step=0; fir.pos=1; q.push(fir);
while(!q.empty()){
node hd=q.front(); q.pop();
if(hd.pos==n){
ans=hd.step;
break;
}
if(hd.step>10000) break;
for(int i=head[hd.pos];i!=0;i=nxt[i]){
if((hd.nowkey&roadkey[i])==roadkey[i]){
node tmp;
tmp.nowkey=(hd.nowkey|roomkey[to[i]]);
tmp.pos=to[i];
tmp.step=hd.step+1;
if(ex[tmp.pos][tmp.nowkey]) continue;
ex[tmp.pos][tmp.nowkey]=true;
q.push(tmp);
}
}
}
if(ans==inf) printf("No Solution"); else printf("%d",ans);
return 0;
}