例1:电路维修
类型:最短路
题目 //这两题的题面相同
题解
边
权
有
0
和
1
两
种
,
所
以
刷
S
P
F
A
。
边权有0和1两种,所以刷SPFA。
边权有0和1两种,所以刷SPFA。
S
P
F
A
加
上
优
化
后
才
能
过
,
否
则
会
被
卡
。
SPFA加上优化后才能过,否则会被卡。
SPFA加上优化后才能过,否则会被卡。
当
然
,
S
P
F
A
这
个
算
法
本
身
也
是
可
以
卡
的
。
当然,SPFA这个算法本身也是可以卡的。
当然,SPFA这个算法本身也是可以卡的。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=505,tt=maxn*maxn;
int n,m,mp[maxn][maxn],dis[maxn][maxn],til,hea,vis[maxn][maxn],INF;
struct js{
int x,y;
void gv(int &xx,int &yy){xx=x,yy=y;}
}q[tt];
int gtc()
{
char ch=getchar();
while (ch!='\\'&&ch!='/') ch=getchar();
return ch=='/';
}
bool pd(int x,int y){return x<1||y<1||x>n||y>m;}
void put(int x,int y,int L)
{
if (dis[x][y]<=L) return;dis[x][y]=L;
if (vis[x][y]) return;vis[x][y]=1;
if (++til>=tt) til-=tt;q[til]=(js){x,y};
int nxt=hea+1;if (nxt>=tt) nxt-=tt;
int X=q[nxt].x,Y=q[nxt].y;
if (dis[x][y]<=dis[X][Y]) swap(q[til],q[nxt]);
}
void bfs()
{
memset(dis,63,sizeof dis);INF=**dis;
dis[1][1]=0;q[til=1]=(js){1,1};
while (hea!=til)
{
if (++hea>=tt) hea-=tt;
int x,y,X,Y,p;
q[hea].gv(x,y);
X=x-1,Y=y-1,p=mp[x-1][y-1];
if (!pd(X,Y)) put(X,Y,dis[x][y]+p);
X=x+1,Y=y+1,p=mp[x][y];
if (!pd(X,Y)) put(X,Y,dis[x][y]+p);
X=x+1,Y=y-1,p=mp[x][y-1]^1;
if (!pd(X,Y)) put(X,Y,dis[x][y]+p);
X=x-1,Y=y+1,p=mp[x-1][y]^1;
if (!pd(X,Y)) put(X,Y,dis[x][y]+p);
vis[x][y]=0;
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j) mp[i][j]=gtc();
++n,++m;bfs();
if (dis[n][m]!=INF) printf("%d\n",dis[n][m]);else printf("NO SOLUTION");
return 0;
}
例2:魔板
类型:状态估计
题目
题解
事 实 上 只 有 8 ! 种 状 态 , 也 就 是 说 我 们 之 间 B F S 就 可 以 了 。 事实上只有 8! 种状态,也就是说我们之间 BFS 就可以了。 事实上只有8!种状态,也就是说我们之间BFS就可以了。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=10,maxt=40325,maxs=134217728;
#define MOD 10007
#define MOD2 (maxt<<1)
int a[maxn],b[maxn],c[maxn],q[maxt],dis[maxt],lst[maxt],tt,til,hea,end,str,nxt,D;
char ans[maxt];
int ran(){return 1ll*rand()%MOD*rand()%MOD;}
int get(int *a){int sum=0;for (int i=0;i<8;++i) sum=sum<<3|a[i];return sum;}
void put(int sum,int *a){for (int i=7;i>=0;--i) a[i]=sum&7,sum>>=3;}
struct js{
js* son[2];
int x,fix;
js(){son[0]=son[1]=NULL;}
void cs(){son[0]=son[1]=NULL;}
}*rot=NULL;
js que[maxt<<1];
void rotate(js*&o,int k)
{
js *u=o->son[k];
o->son[k]=u->son[k^1];
u->son[k^1]=o;
o=u;
}
void insert(js*&o,int x)
{
if (o==NULL) {++tt;if(tt>MOD2)tt-=MOD2;que[tt].cs();o=&que[tt];o->x=x;o->fix=rand()%MOD;return;}
int p=x>o->x;
insert(o->son[p],x);
if (o->son[p]->fix<o->fix) rotate(o,p);
}
bool fin(js*&o,int x){if (o==NULL) return false;if (o->x==x) return true;return fin(o->son[x>o->x],x);}
void prt(int x)
{
if (lst[x]) prt(lst[x]),putchar(ans[x]);
}
void bfs()
{
insert(rot,str);q[++til]=str;int sum;
if (str==end) {printf("0\n");exit(0);}
while (hea!=til)
{
sum=q[++hea];D=dis[hea]+1;
put(sum,a);
if (hea==154){
int i=10;
}
b[0]=a[7];b[1]=a[6];b[2]=a[5];b[3]=a[4];
b[4]=a[3];b[5]=a[2];b[6]=a[1];b[7]=a[0];
if (!fin(rot,nxt=get(b)))
{
q[++til]=nxt;dis[til]=D;lst[til]=hea;ans[til]='A';
if (nxt==end) {printf("%d\n",D);prt(til);return;}
insert(rot,nxt);
}
b[0]=a[3];b[1]=a[0];b[2]=a[1];b[3]=a[2];
b[4]=a[5];b[5]=a[6];b[6]=a[7];b[7]=a[4];
if (!fin(rot,nxt=get(b)))
{
q[++til]=nxt;dis[til]=D;lst[til]=hea;ans[til]='B';
if (nxt==end) {printf("%d\n",D);prt(til);return;}
insert(rot,nxt);
}
b[0]=a[0];b[1]=a[6];b[2]=a[1];b[3]=a[3];
b[4]=a[4];b[5]=a[2];b[6]=a[5];b[7]=a[7];
if (!fin(rot,nxt=get(b)))
{
q[++til]=nxt;dis[til]=D;lst[til]=hea;ans[til]='C';
if (nxt==end) {printf("%d\n",D);prt(til);return;}
insert(rot,nxt);
}
}
}
int main()
{
tt=til=hea=0;
srand(time(0));
for (int i=0;i<8;++i) scanf("%d",c+i),b[i]=i,--c[i];
str=get(b);end=get(c);
bfs();
return 0;
}
例3:Knight Moves
类型:模拟
题目
题解
小 心 ! 听 说 数 据 范 围 与 题 目 面 不 符 合 , 跳 水 。 小心!听说数据范围与题目面不符合,跳水。 小心!听说数据范围与题目面不符合,跳水。
题解
#include<bits/stdc++.h>
using namespace std;
const int maxn=305,f[8][2]={{1,2},{2,1},{-1,2},{2,-1},{1,-2},{-2,1},{-1,-2},{-2,-1}};
int L,x,y,dis[maxn][maxn],til,hea;
struct js{
int x,y;
}q[maxn*maxn];
bool pd(int x,int y){return x<0||x>L|y<0||y>L||dis[x][y]<1e9;}
void bfs()
{
for(int a,b,X,Y;hea!=til;)
{
a=q[++hea].x;b=q[hea].y;
for (int i=0;i<8;++i)
{
if (!pd(X=a+f[i][0],Y=b+f[i][1]))
{
q[++til]=(js){X,Y};
dis[X][Y]=dis[a][b]+1;
if (X==x&&Y==y) return;
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d%d",&L,&x,&y);
for (int i=0;i<=L;++i)
for (int j=0;j<=L;++j) dis[i][j]=1e9;
dis[x][y]=hea=0;q[til=1]=(js){x,y};
scanf("%d%d",&x,&y);bfs();
printf("%d\n",dis[x][y]);
}
return 0;
}
练习1:棋盘游戏
类型:状态设计
题面
题解
总
状
态
数
是
很
少
的
,
可
以
全
部
遍
历
一
遍
(
最
差
的
情
况
)
总状态数是很少的,可以全部遍历一遍(最差的情况)
总状态数是很少的,可以全部遍历一遍(最差的情况)
但
是
状
态
的
转
移
最
好
直
接
在
二
进
制
下
进
行
(
把
状
态
看
成
一
个
二
进
制
数
,
直
接
进
行
转
移
)
但是状态的转移最好直接在二进制下进行(把状态看成一个二进制数,直接进行转移)
但是状态的转移最好直接在二进制下进行(把状态看成一个二进制数,直接进行转移)
这
样
会
快
一
些
这样会快一些
这样会快一些
其
实
代
码
中
的
枚
举
,
二
进
制
下
哪
位
为
1
的
循
环
都
可
以
省
略
。
其实代码中的枚举,二进制下哪位为1的循环都可以省略。
其实代码中的枚举,二进制下哪位为1的循环都可以省略。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=65550;
int a[20],str,end,q[maxn],dis[maxn],til,hea;
int cc()
{
char c=getchar();
while (c!='0'&&c!='1') c=getchar();
return c-'0';
}
int main()
{
str=end=til=hea=0;
memset(dis,63,sizeof dis);int INF=*dis;
for (int i=0;i<16;++i) a[i]=cc();
for (int i=15;i>=0;--i) str=str<<1|a[i];
for (int i=0;i<16;++i) a[i]=cc();
for (int i=15;i>=0;--i) end=end<<1|a[i];
q[++til]=str;dis[str]=0;
while (hea!=til)
{
int x=q[++hea],y,z,D;D=dis[x]+1;
for (int i=0;i<4;++i)
for (int j=0;j<4;++j)
{
if (x&1<<(i<<2)+j)
{
y=x^1<<(i<<2)+j;
if (i &&(z=y^1<<(i-1<<2)+j)>y&&dis[z]>D) dis[z]=D,q[++til]=z;
if (i<3&&(z=y^1<<(i+1<<2)+j)>y&&dis[z]>D) dis[z]=D,q[++til]=z;
if (j &&(z=y^1<<(i<<2)+j-1)>y&&dis[z]>D) dis[z]=D,q[++til]=z;
if (j<3&&(z=y^1<<(i<<2)+j+1)>y&&dis[z]>D) dis[z]=D,q[++til]=z;
}
}
if (dis[end]!=INF) break;
}
printf("%d",dis[end]);
return 0;
}
练习3:移动玩具
类型:压位
题目
题解
跳 水 跳水 跳水
代码
#include<bits/stdc++.h>
using namespace std;
const int maxs=131075,f[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int n,str,end,vis[maxs],q[maxs],dis[maxs],hea,til;char s[100];
int gt()
{
char ch=getchar();while (ch!='0'&&ch!='1') ch=getchar();return ch-'0';
}
int main()
{
for (int i=0;i<16;++i) str|=gt()<<i;
for (int i=0;i<16;++i) end|=gt()<<i;
if (end==str) {printf("0");return 0;}
memset(dis,63,sizeof dis);dis[str]=hea=til=0;q[++til]=str;
while (hea!=til)
{
int now,otr=0,nxt,x,y,a,b,DIS;
now=q[++hea];DIS=dis[now]+1;
for (int i=now&-now;now;i=now&-now)
{
int j=log2(i);y=j&3;x=j>>2;now^=i;
for (int k=0;k<4;++k)
{
a=x+f[k][0];b=y+f[k][1];
if (a<0||b<0||a>3||b>3||(now&1<<(a<<2|b))) continue;
nxt=now^otr^1<<(a<<2|b);
if (nxt==end) {printf("%d\n",DIS);return 0;}
if (dis[nxt]>DIS) dis[q[++til]=nxt]=DIS;
}
otr^=i;
}
}
return 0;
}
练习4:山峰和山谷 Ridges and Valleys
类型 BFS
题目
题解
有
一
种
思
路
是
按
照
每
个
格
子
的
高
度
排
序
,
然
后
从
低
到
高
遍
历
。
有一种思路是按照每个格子的高度排序,然后从低到高遍历。
有一种思路是按照每个格子的高度排序,然后从低到高遍历。
其
实
完
全
没
必
要
这
样
(
虽
然
这
样
貌
似
也
能
过
)
,
我
们
只
需
要
每
次
遍
历
相
同
高
度
的
连
通
块
,
如
果
发
现
连
通
块
周
围
存
在
更
高
的
节
点
,
那
么
它
一
定
不
是
山
峰
(
山
谷
同
理
)
。
其实完全没必要这样(虽然这样貌似也能过),我们只需要每次遍历相同高度的连通块,如果发现连通块周围存在更高的节点,那么它一定不是山峰(山谷同理)。
其实完全没必要这样(虽然这样貌似也能过),我们只需要每次遍历相同高度的连通块,如果发现连通块周围存在更高的节点,那么它一定不是山峰(山谷同理)。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005,f[8][2]={{1,0},{0,1},{-1,0},{0,-1},{1,1},{-1,1},{1,-1},{-1,-1}};
char fil[100000],*A=fil,*B=fil;
int n,m,mp[maxn][maxn],vis[maxn][maxn],anshi,anslo,til,hea;
struct js{
int x,y,h;
}q[maxn*maxn];;
char gt(){return A==B&&(B=(A=fil)+fread(fil,1,100000,stdin),A==B)?EOF:*A++;}
int rad()
{
int ret=0,f=1;char ch=gt();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gt();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
return ret*f;
}
void bfs(int x,int y,int h)
{
hea=0;q[til=1]=(js){x,y,h};vis[x][y]=1;
int sf=1,sg=1,X,Y;
while (hea!=til)
{
x=q[++hea].x,y=q[hea].y;h=q[hea].h;
for (int i=0;i<8;++i)
{
X=x+f[i][0];Y=y+f[i][1];
if (X<1||Y<1||X>n||Y>n) continue;
if (mp[X][Y]>h) sf=0;
if (mp[X][Y]<h) sg=0;
if (mp[X][Y]!=h||vis[X][Y]) continue;
q[++til]=(js){X,Y,mp[X][Y]};vis[X][Y]=1;
}
}
anshi+=sf;anslo+=sg;
}
int main()
{
n=rad();m=0;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j) mp[i][j]=rad();
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j) if (!vis[i][j]) bfs(i,j,mp[i][j]);;
printf("%d %d",anshi,anslo);
return 0;
}