思路:
一条蛇,满图跑,dp状态太多,只能搜索,分析一下时间复杂度,发现每个点最多出队一次,搜索可行!
怎么搜呢?发现只有三种情况
1.没被身体挡住,直接走
2.被身体挡住了,绕道走
3.被身体挡住了,把蛇砍掉一截再走
维护一个dw数组,表示蛇初始状态时,每个点到尾巴的距离,例如一条长度为3的蛇,从头到尾依次是 (1,1),(1,2),(1,3)
那么dw[1][1]=2,d[1][2]=1,d[1][3]=0,其余的都为0
维护dw数组有什么用?
当一个点搜到了蛇初始状态的某个点时,就要判断,蛇尾离开没,如过离开了就是上述情况1,否则是情况2,3
为什么其余点dw设为0?
因为其余的点必然是蛇头先到,不存在蛇尾有没有离开是情况,所以都设为0
怎么知道什么时候是情况2,什么是时候是情况3?
这个其实不需要人为考虑,交给优先多列考虑就好了
将bfs里面的队列改为优先队列,利用dijsktra的思想,每个点第一次出队一定是起点到该点合法的最短路
其余细节:1.压维好写一点
2.答案要对2的64次方取模,可以利用unsigned long long 自然溢出,也可以用__int128
ac代码如下(有点冗余):
#include<bits/stdc++.h> #define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) //#define int long long #define double long double #define pii pair<int,int> #define se second #define fi first using namespace std; typedef unsigned long long ULL; const int N=1e7+10,INF=4e18; const double eps=1e-15; char g[N]; int d[N],dw[N],st[N]; int m,n,k,root,cnt; int vx[]={1,-1,0,0}; int vy[]={0,0,-1,1}; int get(int a,int b) { return a*3010+b; } pii fget(int a) { int x=a/3010; int y=a%3010; return {x,y}; } void bfs() { priority_queue<pii,vector<pii>,greater<pii>> pr; d[root]=0; pr.push({0,root}); while(pr.size()) { auto t=pr.top(); pr.pop(); int var = t.se, dis=t.fi; if(st[var]) continue; st[var]=1; if((--cnt)==0) break; pii now=fget(var); for(int i=0;i<4;i++) { int x=now.fi+vx[i]; int y=now.se+vy[i]; if(x<=0||x>m||y<=0||y>n) continue; if(g[get(x,y)]=='#') continue; int dd; if(dis+1>dw[get(x,y)]) dd=dis+1; else dd=dw[get(x,y)]+1; if(d[get(x,y)]>dd) { d[get(x,y)]=dd; pr.push({dd,get(x,y)}); } } } } void solve() { cin>>m>>n>>k; for(int i=1;i<=k;i++) { int a,b; cin>>a>>b; dw[get(a,b)]=k-i; if(i==1) root=get(a,b); } for(int i=1;i<=m;i++) for(int j=1;j<=n;j++){ d[get(i,j)]=0x3f3f3f3f; cin>>g[get(i,j)]; if(g[get(i,j)]=='.') cnt++; } bfs(); ULL res=0; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) { if(d[get(i,j)]!=0x3f3f3f3f) res+=(long long)d[get(i,j)]*(long long)d[get(i,j)]; } cout<<res<<'\n'; } signed main(){ int T; IOS; T=1; while(T--) solve(); return 0; }