1. 程式人生 > >bzoj1698 / P1606 [USACO07FEB]白銀蓮花池Lilypad Pond

bzoj1698 / P1606 [USACO07FEB]白銀蓮花池Lilypad Pond

P1606 [USACO07FEB]白銀蓮花池Lilypad Pond

轉化為最短路求解

放置蓮花的方法如果直接算會有重複情況。

於是我們可以先預處理和已有蓮花之間直接互相可達的點,將它們連邊(對,忽略它們)。

於是剩下的就是邊權為1的邊了。

醬紫我們就成功轉化為了邊權問題。

藍後跑跑最短路順便計個數就解決了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace
std; 6 #define N 50 7 #define inf 2e9 8 typedef long long ll; 9 struct data{ 10 int d,u; 11 data(){} 12 data(int A,int B): 13 d(A),u(B){} 14 bool operator < (const data &tmp) const{ 15 return d>tmp.d; 16 } 17 };priority_queue <data> h;
18 const int d1[8]={2,2,-2,-2,1,-1,1,-1}; 19 const int d2[8]={-1,1,-1,1,2,2,-2,-2}; 20 int n,m,S,T,id[N][N],e[N][N],d[N*N]; 21 int cnt,hd[N*N],nxt[100009],ed[N*N],poi[100000]; 22 bool vis[N][N]; ll t[N*N]; 23 void adde(int x,int y){ 24 nxt[ed[x]]=++cnt; hd[x]=hd[x]?hd[x]:cnt; 25 ed[x]=cnt; poi[cnt]=y;
26 } 27 void draw(int tt,int x,int y){ 28 vis[x][y]=1; 29 for(int i=0,r1,r2;i<8;++i){ 30 r1=x+d1[i],r2=y+d2[i]; 31 if(r1<1||r1>n||r2<1||r2>m||vis[r1][r2]) continue; 32 if(e[r1][r2]==1) draw(tt,r1,r2); 33 else vis[r1][r2]=1,adde(tt,id[r1][r2]); 34 } 35 } 36 int main(){ 37 scanf("%d%d",&n,&m); 38 for(int i=1,r=0;i<=n;++i) 39 for(int j=1;j<=m;++j){ 40 scanf("%d",&e[i][j]); 41 id[i][j]=++r; d[r]=inf; 42 if(e[i][j]==3) S=r; 43 if(e[i][j]==4) T=r; 44 } 45 for(int i=1;i<=n;++i) 46 for(int j=1;j<=m;++j) 47 if(e[i][j]==0||e[i][j]==3){ 48 memset(vis,0,sizeof(vis)); 49 draw(id[i][j],i,j);//沒有蓮花:就和其他所有與該點之間只要加一朵蓮花就可達的點,連一條邊權1的邊。 50 } 51 h.push(data(d[S]=0,S)); t[S]=1; 52 while(!h.empty()){ 53 data x=h.top(); h.pop(); 54 if(x.d!=d[x.u]) continue; 55 for(int i=hd[x.u];i;i=nxt[i]){ 56 int to=poi[i]; 57 if(d[to]==d[x.u]+1){ 58 t[to]+=t[x.u]; 59 }else if(d[to]>d[x.u]+1){ 60 d[to]=d[x.u]+1; 61 t[to]=t[x.u]; 62 h.push(data(d[to],to)); 63 } 64 } 65 } 66 if(d[T]==inf) printf("-1"); 67 else printf("%d\n%lld",d[T]-1,t[T]);//減去終點多算的一朵 68 return 0; 69 }
View Code