搜索专题杂练
2022/10
主要是搜索专题的好题
洛谷 P2329 [SCOI2005]栅栏
二分+dfs
洛谷 P1120 小木棍
爆搜+剪枝
用桶存一下木棍,记录最大最小。
深搜的状态:
\(\text{sum-}\)目前拼的木棍长度
\(\text{step-}\)拼到第几根木棍
\(\text{MAX-}\) 上一个拼的值(最大边界)
剪枝:
-
从大往小拼
-
从最大的小木棍开始枚举木棍长度
-
如果遇到目前是整数个拼好的木棍,且拼不下去了,直接跳出循环
#include<bits/stdc++.h>
using namespace std;
int n,x,I,m,cnt,Sum,maxn,minn=100;
int t[100];
void dfs(int sum,int step,int MAX)
{
if(step==Sum/I)
{
printf("%d",I);
exit(0);
}
if(sum==I)
{
dfs(0,step+1,maxn);
return;
}
for(int i=MAX;i>=minn;i--)
{
if(t[i]==0||i+sum>I) continue;
t[i]--;
dfs(sum+i,step,i);
t[i]++;
if(sum==0||sum+i==I) break;
}
return;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
cnt++,t[x]++,Sum+=x;
maxn=max(maxn,x);
minn=min(minn,x);
}
for(I=maxn;I<=Sum;I++)
{
if(Sum%I!=0) continue;
dfs(0,0,maxn);
}
printf("%d",Sum);
return 0;
}
洛谷 P1763 埃及分数
迭代加深搜索
注:当输入为 570 877
时需要跑很久。
当前搜到还剩 \(\Large\frac{a}{b}\) 的时候,最大边界为 \(\Large \frac{a}{b}-\frac{1}{i}\) 即 \(\Large \frac{ai-b}{bi}\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
void yf(int &x,int &y)
{
int GCD=__gcd(x,y);
x/=GCD,y/=GCD;
}
bool flag;
int minn=-1,k;
int nans[1010],ans[1010];
void dfs(int now,int a,int b,int lst) //还剩的分数个数;还剩的分数;上一个分数的分母
{
yf(a,b);
if(now==1)
{
if(a==1&&b>lst)
{
if(flag==1&&b>nans[1]) return;
flag=1,nans[1]=b;
memcpy(ans,nans,k<<5);
}
return;
}
for(int i=lst;i<=(b*now/a);i++) //边界为 a/(b*now) 的倒数(1/a/(b*now))
{
nans[now]=i;
dfs(now-1,a*i-b,b*i,i);
}
}
int x,y;
signed main()
{
cin>>x>>y;
yf(x,y);
while(!flag)
{
k++;
dfs(k,x,y,1);
}
for(int i=k;i>=1;i--) cout<<ans[i]<<" ";
return 0;
}
POJ 3322 Bloxorz
纯纯的爆搜。
几点注意事项:
-
记录开始位置的时候记得加上是否判断到过的标记,要不然会记录错位置。
-
记录 $1\times 1 \times 2 $ 小长方体的上边的或左边的正方体的位置。
-
记得注意脆弱的格子。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN=517;
struct Node
{
int x,y,z;
};
queue<pair<Node,int>>q;
char map[MAXN][MAXN];
int vis[MAXN][MAXN][3];
int dis[3][4][3]=
{
{{-2,0,1},{1,0,1},{0,-2,2},{0,1,2}},
{{0,-1,1},{0,1,1},{-1,0,0},{2,0,0}},
{{0,-1,0},{0,2,0},{-1,0,2},{1,0,2}}
};
int n,m;
int sx,sy,ex,ey;
pair<Node,int> mkpair(Node x,int y)
{
return pair<Node,int>(x,y);
}
Node stNode()
{
Node tmp;
tmp.x=sx,tmp.y=sy;
if(map[sx+1][sy]=='X') tmp.z=1;
else if(map[sx][sy+1]=='X') tmp.z=2;
else tmp.z=0;
return tmp;
}
bool check(Node a)
{
if(a.x<=0||a.x>n||a.y<=0||a.y>m) return false;
if(map[a.x][a.y]=='#') return false;
if(a.z==0)
{
if(map[a.x][a.y]=='E') return false;
return true;
}
int x2=a.x,y2=a.y;
if(a.z==1) x2++;
else y2++;
if(x2<=0||x2>n||y2<=0||y2>m) return false;
if(map[x2][y2]=='#') return false;
return true;
}
bool end(Node a)
{
return a.z==0&&a.x==ex&&a.y==ey;
}
void bfs()
{
q.push(mkpair(stNode(),0));
while(!q.empty())
{
Node tmp=q.front().first;
int depth=q.front().second;
q.pop();
for(int i=0;i<4;i++)
{
Node nxt;
nxt.x=tmp.x+dis[tmp.z][i][0],nxt.y=tmp.y+dis[tmp.z][i][1],nxt.z=dis[tmp.z][i][2];
if(check(nxt)&&!vis[nxt.x][nxt.y][nxt.z])
{
vis[nxt.x][nxt.y][nxt.z]=1;
q.push(mkpair(nxt,depth+1));
if(end(nxt))
{
printf("%d\n",depth+1);
return;
}
}
}
}
printf("Impossible\n");
}
int main()
{
while(true)
{
bool unfined=true;
scanf("%d%d",&n,&m);
if(n==0&&m==0) break;
for(int i=1;i<=n;i++)
{
scanf("%s", (map[i]+1));
for(int j=1;j<=m;j++)
{
if(map[i][j]=='X'&&unfined)
{
unfined=false;
sx=i,sy=j;
}
else if(map[i][j]=='O') ex=i,ey=j;
}
}
memset(vis,0,sizeof(vis));
while(!q.empty()) q.pop();
bfs();
}
return 0;
}
UVA589 Pushing Boxes
教练说的 Boss 战,细节还是蛮多的。
主要思路是双重 bfs,一个搜箱子,另一个根据箱子的移动搜人。
#include<bits/stdc++.h> //没hjl我这道题就调不出来了qwq
#define pii pair<int,int> //3.5kb的大臭代码
using namespace std;
int n, m, k;
string ans="-1";
bool unfined = true;
int bx, by; //box
int px, py; //ppl
int zx, zy; //to
char c[25][25];
struct Find
{
int px, py, bx, by;
string ans = "";
};
void print()
{
cout << "Maze #" << k << endl;
return;
}
char tocharA(int x) //数字转字符
{
if(x == 1) return 'E';
else if (x == 2) return 'W';
else if (x == 3) return 'S';
else return 'N';
}
char tochara(int x)
{
if(x == 1) return 'e';
else if (x == 2) return 'w';
else if (x == 3) return 's';
else return 'n';
}
string Min(string a,string b) //统计哪个方法更优
{
if(a=="-1") return b;
int fir1=0,sec1=0,fir2=0,sec2=0;
for(int i=0;i<a.size();i++)
fir1+=(a[i]>='A' && a[i]<='Z'),sec1+=(a[i]>='a' && a[i]<='z');
for(int i=0;i<b.size();i++)
fir2+=(b[i]>='A' && b[i]<='Z'),sec2+=(b[i]>='a' && b[i]<='z');
if(fir1!=fir2) return (fir1<fir2?a:b);
else return (sec1<sec2?a:b);
}
namespace bfs
{
bool vis[25][25];
bool v[25][25][10];
int bX[] = {0, 0, 0, 1, -1};
int bY[] = {0, 1, -1, 0, 0};
bool check(int x, int y)
{
if(x < 1 || x > n || y < 1 || y > m) return false;
if(c[x][y] == '#') return false;
//这个地方我原本写的是 vis[x][y],写双重搜索的时候就容易出这种错
return true;
}
pair<bool,string> pbfs(int ppx, int ppy, int bbx, int bby, int ttx, int tty) //搜索人,参数为人当前位置;箱子当前位置;人要去的位置
{
memset(vis, 0, sizeof vis);
queue<Find> q;
Find p;
p.px = ppx, p.py = ppy;
q.push(p);
vis[ppx][ppy] = 1;
if(ppx == ttx && ppy == tty) return make_pair(1, "");
while(!q.empty())
{
Find now = q.front();
q.pop();
if(now.px == ttx && now.py == tty)
{
return make_pair(1, now.ans); //找到了传参
}
for(int i = 1; i <= 4; i++)
{
int tx = now.px + bX[i];
int ty = now.py + bY[i];
if(check(tx, ty) && !vis[tx][ty] && !(tx == bbx && ty == bby))
{
Find qq;
qq.px = tx, qq.py = ty;
qq.ans = now.ans + tochara(i); //记录步骤
q.push(qq);
vis[tx][ty] = 1;
}
}
}
memset(vis, 0, sizeof vis);
return make_pair(0, "");
}
void bbfs()
{
queue<Find> q;
Find p;
p.px = px, p.py = py, p.bx = bx, p.by = by;
q.push(p);
while(!q.empty())
{
Find wzh = q.front();
q.pop();
if(wzh.bx == zx && wzh.by == zy)
{
ans=Min(ans,wzh.ans);
unfined = false;
continue;
}
for(int i = 1; i <= 4; i++)
{
int bnx = wzh.bx + bX[i];
int bny = wzh.by + bY[i];
int pnx = wzh.bx - bX[i];
int pny = wzh.by - bY[i];
pair<bool,string> tt = pbfs(wzh.px, wzh.py, wzh.bx, wzh.by, pnx, pny);
if(check(bnx, bny) && check(pnx, pny) && !v[bnx][bny][i] && tt.first) //tt.first 判断是否可以走过去
{
Find tmp;
tmp.bx = bnx, tmp.by = bny;
tmp.px = wzh.bx, tmp.py = wzh.by;
tmp.ans = wzh.ans + tt.second + tocharA(i); //合并步骤(注意:走是比推靠前的)
q.push(tmp);
v[tmp.bx][tmp.by][i] = 1;
}
}
}
}
}using namespace bfs;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
while(cin >> n >> m)
{
ans="-1";
memset(v,0,sizeof(v));
k++;
unfined = true;
if(n == 0 && m == 0) break;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
cin >> c[i][j];
if(c[i][j] == 'S') px = i, py = j;
if(c[i][j] == 'B') bx = i, by = j;
if(c[i][j] == 'T') zx = i, zy = j;
}
bbfs();
if(unfined) print(), cout << "Impossible." << endl; //是否找到答案的标记
else print(),cout<<ans<<endl;
cout << endl;
}
return 0;
}