搜索的几道简单题
1.https://www.luogu.com.cn/problem/P1025
这道题明显使用搜索,但我还是不太会,所以发一篇博客,题目描述
将整数 n 分成 k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如:
n=7,k=3,下面三种分法被认为是相同的。
问有多少种不同的分法。
输入格式
n,k (6<n≤200,2≤k≤6)
首先,这道题的每个位都可以在1n中取一个数,我先想到的是暴力枚举,及暴力枚举每个位的数字,然后条件判断,但我发现,对于测试样例来说,暴力可能会过,时间复杂度是O(n**3),倘若位数再大一点,大概率过不了,可以用搜索搜每个位上的数字,和的范围为021,应该是0~7,大部分都用不上,所以还应该用到剪枝,即sum > 7的时候就return. 代码:
void dfs(int x,int sum)
{
if(sum > n)
return;
if(x == k)
{
if(sum == n)
{
cnt ++;
return;
}
}
for(int i = 1;i <= n;i ++)
{
a[x] = i;//开个数组来模拟每个位的取值
dfs(x + 1,sum + a[x]);
a[x] = 0;
}
}!!!这份代码是错的,比如对于(1,1,5)和(1,5,1)和(5,1,1)会计为3次,我们可以多加入一个start参数来使其只计数为1次,如下,对于重复的但顺序不同的,可以排个序使其单调,从而使三个都为由小到大的(1,1,5),这不会影响带结果的,代码如下://应该也可以单调减来做的
include<bits/stdc++.h>
using namespace std;
int a[10];
int n,k,cnt = 0;
void dfs(int x,int start,int sum)
{
if(sum > n)
return;
if(x == k)
{
if(sum == n){
cnt ++;
}
return;
}
for(int i = start;sum + i <= n;i ++)
{
a[x] = i;
dfs(x + 1,i,sum + a[x]);
a[x] = 0;//回溯
}
}
int main()
{
cin >> n >> k;
dfs(0,1,0);
cout << cnt << endl;
return 0;
}
*****扩展 : 以下是我用单调减的方法来做的,代码如下:
include<bits/stdc++.h>
using namespace std;
int a[10];
int n,k,cnt = 0;
void dfs(int x,int start,int sum)
{
if(sum < 0)
return;
if(x == k)
{
if(sum == 0){
cnt ++;
}
return;
}
for(int i = start;i >= 1;i --)
{
if(sum - i < 0) continue;
else
{
a[x] = i;
dfs(x + 1,i,sum - a[x]);
a[x] = 0;
}
}
}
int main()
{
cin >> n >> k;
dfs(0,n,n);
cout << cnt << endl;
return 0;
}
这个做法千万别多想,就是让sum初始化为n,然后不断减就行。
2.https://www.luogu.com.cn/problem/P1746
这道题唯一的坑便是输入数组时候注意,中间没有空格,所以我用字符转数字来做的,代码如下:
include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,t_x,t_y,e_x,e_y;
int a[N][N];
bool b[N][N];
char c;
queue<pair<int,int>> q;
int dx[4] = {-1,0,0,1};
int dy[4] = {0,1,-1,0};
void bfs(int st,int ed)
{
q.push({st,ed});
b[st][ed] = true;
while(!q.empty())
{
int x = q.front().first,y = q.front().second;
q.pop();
for(int i = 0;i <4;i ++)
{
int xx = x + dx[i];
int yy = y + dy[i];
if(xx < 1 || xx > n || yy < 1 || yy > n || a[xx][yy] == 1 || b[xx][yy]) continue;
a[xx][yy] = a[x][y] + 1;
b[xx][yy] = true;
q.push({xx,yy});
if(xx == e_x && yy == e_y) return ;
}
}
}
int main()
{
cin >> n;
memset(b,false,sizeof(b));
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
/*c = getchar();
while(c == '\n' || c == ' ')
c = getchar();
a[i][j] = c - '0'; */
cin >> c;
a[i][j] = c - '0';
}
}
cin >> t_x >> t_y >> e_x >> e_y;
bfs(t_x,t_y);
if(a[e_x][e_y] == 0 && b[e_x][e_y] == false) cout << "-1" << endl;
else
cout << a[e_x][e_y] << endl;
return 0;
}
3.https://www.luogu.com.cn/problem/P1332
这道题我原本的想法是如下图:
然后对领主们分别的位置取个min值就行,但是下面代码的思路(更好的思路)便是对领主们的位置来说,感染源同时bfs,谁先到即是领主最快感染时间。
不懂得话可以结合下图理解:
在你搜距离2号点的x远的点时是确保距离1号点的x远的点搜过,在你搜距离1号点的x远的点时是确保距离2号点的x - 1远的点搜过.
include<bits/stdc++.h>
using namespace std;
const int N = 510;
int n,m,aa,bb;
int a[N][N];
bool b[N][N];
int dx[4] = {-1,0,0,1};
int dy[4] = {0,1,-1,0};
queue<pair<int,int>> q;
void bfs()
{
while(!q.empty())
{
int x = q.front().first,y = q.front().second;
q.pop();
for(int i = 0;i < 4;i ++)
{
int xx = x + dx[i];
int yy = y + dy[i];
if(xx < 1 || xx > n || yy < 1 || yy > m || a[xx][yy] || b[xx][yy]) continue;
a[xx][yy] = a[x][y] + 1;
b[xx][yy] = true;
q.push({xx,yy});
}
}
}
int main()
{
cin >> n >> m>> aa >> bb;
memset(b,false,sizeof(b));
memset(a,0,sizeof(a));
for(int i = 1;i <= aa;i ++)
{
int x,y;
cin >> x >> y;
q.push({x,y});
b[x][y] = true;
}
bfs();
for(int i = 1;i <= bb;i ++)
{
int z,z_z;
cin >> z >> z_z;
cout << a[z][z_z] << endl;
}
return 0;
}
4.https://www.luogu.com.cn/problem/P2298
这道题很简单,处理输入并直接转为模板即可。
include<bits/stdc++.h>
using namespace std;
const int N = 2010;
int n,m,s_x,s_y,e_x,e_y;
string st;
int a[N][N];
bool b[N][N];
queue<pair<int,int>> q;
int dx[4] = {-1,0,0,1};
int dy[4] = {0,1,-1,0};
string bfs(int x,int y)
{
q.push({x,y});
b[x][y] = true;
while(!q.empty())
{
int u = q.front().first,z = q.front().second;
q.pop();
for(int i = 0;i < 4;i ++)
{
int xx = u + dx[i];
int yy = z + dy[i];
if(xx < 1 || xx > n || yy < 1 || yy > m || b[xx][yy] || a[xx][yy]) continue;
a[xx][yy] = a[u][z] + 1;
b[xx][yy] = true;
q.push({xx,yy});
if(xx == e_x && yy == e_y) return to_string(a[e_x][e_y]);
}
}
return "No Way!";
}
int main()
{
cin >> n >> m;
memset(b,false,sizeof(b));
for(int i = 1;i <= n;i ++)
{
getline(cin,st);
for(int j = 1; j <= m;j ++)
{
char c = getchar();
if(c == '.') a[i][j] = 0;
if(c == '#') a[i][j] = 1;
if(c == 'm')
{
s_x =i;
s_y = j;
a[i][j] = 0;
}
if(c == 'd')
{
e_x = i;
e_y = j;
a[i][j] = 0;
}
}
}
cout << bfs(s_x,s_y) << endl;
return 0;
}
5.https://www.luogu.com.cn/problem/P1434
题目是滑雪:用到了记忆化搜索,记忆化搜索的大致意思就是把已经搜过的点存起来,有点像Dp里面按一定顺序搜完之后这个点保存的是从这个点起搜的最优解,以后再次搜到这个点时,直接使用,就像一句话说得好:前人种树,后人乘凉的意思,代码如下:
include<bits/stdc++.h>
using namespace std;
define int long long
int n,m;
int a[110][110];
int s[110][110];//step数组
int dfs(int x,int y)
{
if(x < 1 || x > n ||y < 1|| y > m) return 0;
if(s[x][y] != -1)
{
return s[x][y];
}
int t = 0;
if(a[x+1][y] > a[x][y]) t = max(t,dfs(x+1,y) );
if(a[x][y+1] > a[x][y]) t = max(t,dfs(x,y+1) );
if(a[x][y-1] > a[x][y]) t = max(t,dfs(x,y-1) );
if(a[x-1][y] > a[x][y]) t = max(t,dfs(x-1,y) );
return s[x][y] = t + 1;
}
signed main()
{
cin >> n >> m;
for(int i =1; i<= n; i++)
for(int j = 1;j <= m;j ++)
{
cin >> a[i][j];
s[i][j] = -1;
}
int ans = 0;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <=m;j ++)
ans = max(ans,dfs(i,j));
cout << ans;
return 0;
}
下面是我改进了一下代码,切记单个点不能搜索时直接赋值为1(即搜索时每艘一次时的t初始化为1),代码如下:
include
include
using namespace std;
typedef long long ll;
ll R,C;
ll a[110][110],s[110][110];
int dfs(int i, int j) {
if (i < 1 || i > R || j < 1 || j > C) return 0;
if (s[i][j] != -1) {
return s[i][j];
}
int t = 1;
if (a[i + 1][j] < a[i][j]) t = max(t, dfs(i + 1, j) + 1);
if (a[i - 1][j] < a[i][j]) t = max(t, dfs(i - 1, j) + 1);
if (a[i][j - 1] < a[i][j]) t = max(t, dfs(i, j - 1) + 1);
if (a[i][j + 1] < a[i][j]) t = max(t, dfs(i, j + 1) + 1);
return s[i][j] = t;
}
int main()
{
cin >> R >> C;
for(int i = 1;i <= R;i ++)
for(int j = 1;j <= C;j ++)
{
cin >> a[i][j];
s[i][j] = -1;
}
int ans = 0;
for(int i = 1;i <= R;i ++)
for(int j = 1;j <= C;j ++)
ans = max(ans,dfs(i,j));
cout << ans ;
return 0;
}
6.https://www.luogu.com.cn/problem/P1141
这道题直接用搜索做,但是直接搜会超时,加入连通块的一些思路,开一个数组来存连通块的序号,最后直接得到答案,代码如下:
include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,m,k=1,res;
char g[N][N];
int vis[N][N];
int ans[1000010];
int dx[4]={-1,0,0,1};
int dy[4]={0,1,-1,0};
queue<pair<int,int>> q;
void dfs(int x,int y)
{
q.push({x,y});
while(q.size())
{
auto p=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int xx=p.first+dx[i];
int yy=p.second+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&g[xx][yy]!=g[p.first][p.second]&&!vis[xx][yy])
{
vis[xx][yy]=k;
res++;
q.push({xx,yy});
}
}
}
}
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>g[i][j];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(!vis[i][j])
{
res=1;
vis[i][j]=k;
dfs(i,j);
ans[k]=res;
k++;
}
}
}
for(int i=1;i<=m;i++)
{
int z,z1;
cin>>z>>z1;
cout<<ans[vis[z][z1]]<<endl;
}
return 0;
}
7.https://www.luogu.com.cn/problem/P1219
题目中有注释,有个地方可以return也可以没有return,代码如下:
include<bits/stdc++.h>
using namespace std;
const int N = 30;
int n,res=0;
int x_x[N],y_y[N],x_y[N],y_x[N];
void dfs(int x)
{
if(x == n)
{
res++;
if(res < 4)
{
for(int i=0;i<n;i++)
cout<<x_x[i]<<" ";
cout<<endl;
}
//return;
}
for(int i=1;i<=n;i++)
{
if(!y_y[i] && !x_y[x+i] && !y_x[n-x+i])
{
x_x[x]=i;
y_y[i]=1;
x_y[x+i]=1;
y_x[n-x+i]=1;
dfs(x+1);
y_y[i]=0;
x_y[x+i]=0;
y_x[n-x+i]=0;
}
}
}
int main()
{
cin >> n;
fill(begin(x_x),end(x_x),0);
fill(begin(x_x),end(x_x),0);
fill(begin(x_x),end(x_x),0);
fill(begin(x_x),end(x_x),0);
dfs(0);
cout<<res;
return 0;
}
8.https://www.luogu.com.cn/problem/P1443
题目马的遍历,就是模板的一个变型,把dx和dy数组改一下就行了。代码如下:
include<bits/stdc++.h>
using namespace std;
const int N = 500;
int n,m,x,y;
int a[N][N];
bool b[N][N];
queue<pair<int,int>> q;
int dx[8]={-2,-2,-1,-1,1,1,2,2};
int dy[8]={-1,1,-2,2,2,-2,1,-1};
void dfs(int x,int y)
{
memset(a,-1,sizeof(a));
memset(b,false,sizeof(b));
b[x][y] = true;
a[x][y] = 0;
q.push({x,y});
while(q.size())
{
auto p = q.front();
q.pop();
for(int i=0;i<8;i++)
{
int xx = p.first+dx[i];
int yy = p.second+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&!b[xx][yy])
{
a[xx][yy]=a[p.first][p.second]+1;
b[xx][yy]=true;
q.push({xx,yy});
}
}
}
}
int main()
{
cin >> n >> m >> x >> y;
dfs(x,y);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
printf("%-6d",a[i][j]);
printf("\n");
}
return 0;
}
我尝试拿fill来做,但就会编译错误,用memset可以,只要能做出来题便可。