【笔记】搜索
题单
简单记录一下较典型的
0X00 P1123 取数游戏
考虑从上往下,从左往右依次取数。每次取完一个数将其标记掉,然后下次取前看周围八个是否有被标记的,没有就可以取,否则跳过。用
因为多测,所以记得清空。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int T,n,m,a[10][10],ans;
int X[8]={1,1,1,-1,-1,-1,0,0},Y[8]={1,-1,0,1,-1,0,-1,1};
bool mark[10][10];
void dfs(int x,int y,int cnt){
if(y>m){
dfs(x+1,1,cnt);
return ;
}
if(x>n){
ans=max(ans,cnt);
return ;
}
bool fl=0;
for(int i=0;i<8;i++){
if(mark[x+X[i]][y+Y[i]]){
fl=1;
break;
}
}
if(!fl){
mark[x][y]=1;
dfs(x,y+1,cnt+a[x][y]);
mark[x][y]=0;
}
dfs(x,y+1,cnt);
}
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin>>a[i][j];
}
ans=0;
memset(mark,0,sizeof(mark));
dfs(1,1,0);
cout<<ans<<endl;
}
int main(){
IOS;TIE;
cin>>T;
while(T--) solve();
return 0;
}
0X01 P1238 走迷宫
一般走迷宫我们选择用
在深搜过程中,用栈记录走过的路径,每次到终点时输出一次就好。
比较坑的地方:本题没有SPJ,优先顺序:左上右下,所以对方向数组的顺序是有要求的,不能随便写!
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int n,m,sx,sy,gx,gy,ss[105][2],tot;
int X[4]={0,-1,0,1},Y[4]={-1,0,1,0};
int a[105][105];
char c;
bool mark[105][105],fl;
void print(){
fl=1;
for(int i=1;i<=tot;i++) cout<<"("<<ss[i][0]<<","<<ss[i][1]<<")->";
cout<<"("<<gx<<","<<gy<<")";
}
void dfs(int x,int y){
if(x==gx&&y==gy){
print();
return ;
}
for(int i=0;i<4;i++){
int xx=x+X[i],yy=y+Y[i];
if(mark[xx][yy]) continue ;
if(xx>=1&&xx<=n&&yy>=1&&yy<=m){
ss[++tot][0]=x,ss[tot][1]=y;
mark[x][y]=1;
dfs(xx,yy);
mark[x][y]=0;
tot--;
}
}
}
int main(){
IOS;TIE;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
mark[i][j]=(a[i][j]==0)?1:0;
}
}
cin>>sx>>sy>>gx>>gy;
dfs(sx,sy);
if(!fl) cout<<-1;
return 0;
}
0X02 UVA572 油田 Oil Deposits
每次找到 @
时,答案 @
全标成 *
。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int n,m,ans;
int X[8]={1,1,1,-1,-1,-1,0,0},Y[8]={1,-1,0,1,-1,0,1,-1};
char c[105][105];
struct node{
int x,y;
};
queue<node> q;
void bfs(int sx,int sy){
q.push({sx,sy});
c[sx][sy]='*';
while(q.size()){
node k=q.front();q.pop();
for(int i=0;i<8;i++){
int xx=k.x+X[i],yy=k.y+Y[i];
if(c[xx][yy]=='@'){
c[xx][yy]='*';
q.push({xx,yy});
}
}
}
}
int main(){
IOS;TIE;
while(1){
cin>>n>>m;
if(!n&&!m) break;
ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin>>c[i][j];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(c[i][j]=='@'){
ans++;
while(q.size()) q.pop();
bfs(i,j);
}
}
}
cout<<ans<<endl;
}
return 0;
}
0X03 P3818 小A和uim之大逃离 II
因为只能瞬移一次,所以可以增加一维标记,表示是否使用过瞬移了。
同时
考虑具体怎么走:
首先,不管当前是否有喝过药水,上下左右四个方向是都可以走的。这时候,下一个点的标记应该和走向它的点相同,延续当前标记状态。
如果当前是未标记,则可以使用药水走向
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int n,m,d,r,ans=1e9;
int X[4]={0,0,1,-1},Y[4]={1,-1,0,0};
bool vis[1205][1205][2];
char c;
struct node{
int x,y,ans;
bool fl;
};
queue<node> q;
void bfs(){
q.push({1,1,0,0});
vis[1][1][0]=1;
while(q.size()){
node k=q.front();q.pop();
if(k.x==n&&k.y==m) ans=min(ans,k.ans);
for(int i=0;i<4;i++){
int xx=k.x+X[i],yy=k.y+Y[i];
if(xx<1||xx>n||yy<1||yy>m||vis[xx][yy][k.fl]) continue;
q.push({xx,yy,k.ans+1,k.fl});
vis[xx][yy][k.fl]=1;
}
if(!k.fl){
int xx=k.x+d,yy=k.y+r;
if(xx<1||xx>n||yy<1||yy>m||vis[xx][yy][1]) ;
else{
q.push({xx,yy,k.ans+1,1});
vis[xx][yy][1]=1;
}
}
}
}
int main(){
IOS;TIE;
cin>>n>>m>>d>>r;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>c;
if(c=='#') vis[i][j][0]=vis[i][j][1]=1;
}
}
bfs();
ans==1e9?cout<<-1:cout<<ans;
return 0;
}
0X04 CF767C Garland
首先统计权值总和,若不是
然后从根节点开始
最后答案个数
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int n,id,t[1000005],ans[5],s[1000005];
int cnt,root,sum;
vector<int> a[1000005];
void dfs(int x,int y){
s[x]=t[x];
for(int i=0;i<a[x].size();i++){
int tmp=a[x][i];
if(tmp!=y){
dfs(tmp,x);
s[x]+=s[tmp];
}
}
if(s[x]==sum/3) ans[++cnt]=x,s[x]=0;
}
int main(){
IOS;TIE;
cin>>n;
for(int i=1;i<=n;i++){
cin>>id>>t[i];
sum+=t[i];
if(id) a[id].push_back(i);
else root=i;
}
if(sum%3) cout<<-1;
else{
dfs(root,0);
if(cnt<3) cout<<-1;
else cout<<ans[1]<<' '<<ans[2];
}
return 0;
}
0X05 CF374C Inna and Dima
先考虑如何建图。
首先把每一个字母转化为数字,然后对于每一个点枚举四个方向,如果有下一个字母,就向那个点建一条边,可以用
然后考虑
-
开始搜索的点一定是
D
。 -
已经更新过答案的点就不用搜了,直接
,也算一个剪枝过程。 -
可以选无数个点的情况就是出现环了。所以需要
数组标记本次搜索经过的点,如果走到已经走过的点,直接输出Poor Inna!
结束程序。同时回溯时记得要把 清空。 -
搜索时答案记录为走过的点数比较方便。因为从
D
开始,所以记 。最后若 即输出Poor Dima!
,否则 就是DIMA
的数量。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int n,m,t[1005][1005],dis[1005][1005],ans;
bool vis[1005][1005];
char c;
int X[4]={0,0,1,-1},Y[4]={1,-1,0,0};
struct node{
int x,y;
};
vector<node> a[1005][1005];
void read(){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>c;
if(c=='D') t[i][j]=0;
if(c=='I') t[i][j]=1;
if(c=='M') t[i][j]=2;
if(c=='A') t[i][j]=3;
}
}
}
bool cango(int x1,int y1,int x2,int y2){
if(t[x2][y2]==(t[x1][y1]+1)%4) return 1;
return 0;
}
void build(){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int z=0;z<4;z++){
int xx=i+X[z],yy=j+Y[z];
if(xx>n||xx<1||yy>m||yy<1) continue;
if(cango(i,j,xx,yy)) a[i][j].push_back({xx,yy});
}
}
}
}
void dfs(int sx,int sy){
if(dis[sx][sy]) return;
vis[sx][sy]=1;
dis[sx][sy]=1;
for(int i=0;i<a[sx][sy].size();i++){
int jx=a[sx][sy][i].x,jy=a[sx][sy][i].y;
if(vis[jx][jy]){
cout<<"Poor Inna!";
exit(0);
}
dfs(jx,jy);
dis[sx][sy]=max(dis[sx][sy],dis[jx][jy]+1);
ans=max(ans,dis[sx][sy]);
}
vis[sx][sy]=0;
}
int main(){
IOS;TIE;
cin>>n>>m;
read();
build();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) if(!t[i][j]) dfs(i,j);
}
if(ans<4) cout<<"Poor Dima!";
else cout<<ans/4;
return 0;
}
0X06 P1074 [NOIP2009 提高组] 靶形数独
首先考虑一般数独怎么填。
首先可以按照每行
记三个标记数组:
关于如何快速得到一个坐标
加上了分数的条件,我们只需要一个分数表
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int a[15][15],score[15][15],g[15][15],xx[105],yy[105],now,ans=-1,t;
bool H[15][15],L[15][15],G[15][15];
struct node{
int id,cnt;
}z[15];
bool cmp(node a,node b){
return a.cnt<b.cnt;
}
void pre(){
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
if(i==1||j==1||i==9||j==9) score[i][j]=6;
else if(i==2||j==2||i==8||j==8) score[i][j]=7;
else if(i==3||j==3||i==7||j==7) score[i][j]=8;
else if(i==4||j==4||i==6||j==6) score[i][j]=9;
else score[i][j]=10;
g[i][j]=(i-1)/3*3+(j-1)/3+1;
}
}
for(int i=1;i<=9;i++) z[i].id=i;
}
void dfs(int p,int now){
if(p==t){
ans=max(ans,now);
return ;
}
for(int i=1;i<=9;i++){
if(!H[xx[p]][i]&&!L[yy[p]][i]&&!G[g[xx[p]][yy[p]]][i]){
H[xx[p]][i]=L[yy[p]][i]=G[g[xx[p]][yy[p]]][i]=1;
dfs(p+1,now+i*score[xx[p]][yy[p]]);
H[xx[p]][i]=L[yy[p]][i]=G[g[xx[p]][yy[p]]][i]=0;
}
}
return ;
}
int main(){
pre();
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
cin>>a[i][j];
if(a[i][j]==0) z[i].cnt++;
else{
H[i][a[i][j]]=L[j][a[i][j]]=G[g[i][j]][a[i][j]]=1;
now+=a[i][j]*score[i][j];
}
}
}
sort(z+1,z+10,cmp);
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
if(a[z[i].id][j]==0){
xx[t]=z[i].id,yy[t]=j;
t++;
}
}
}
dfs(0,now);
cout<<ans;
return 0;
}
0X07 P2756 飞行员配对方案问题
匈牙利算法,用于二分图最大匹配。
详细介绍可以看 这篇博客 的【二、匈牙利算法概述】部分,大概就是什么个配对问题。
实现就是
每个点这样取匹配一遍就能得到答案。记得每次匹配前要清空标记。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int n,m,x,y,cnt,ans[105];
bool mark[105];
vector<int> a[105];
bool dfs(int x){
for(int i=0;i<a[x].size();i++){
int tmp=a[x][i];
if(mark[tmp]) continue;
mark[tmp]=1;
if(!ans[tmp]||dfs(ans[tmp])){
ans[tmp]=x;
return 1;
}
}
return 0;
}
int main(){
IOS;TIE;
cin>>n>>m;
while(1){
cin>>x>>y;
if(x==-1&&y==-1) break;
a[x].push_back(y);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) mark[j]=0;
if(dfs(i)) cnt++;
}
cout<<cnt<<endl;
for(int i=1;i<=m;i++){
if(ans[i]) cout<<ans[i]<<' '<<i<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话