传送门
这套题我用的memset比较多,是因为这套题的数据量都比较小,想着省事 ,在实际做题中,如果时间限制短,或者多样例时,强烈不建议用memset,memset时间消耗太大。
A、Red and Black
题解:搜索模板题,dfs和bfs都可以,直接爆搜,能搜到 ’ . ’ 答案就加一
代码:
#include<bits/stdc++.h>
using namespace std;
int ans=0;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int n,m;
string s[22];
bool st[22][22];
void dfs(int u,int v){
ans++;
st[u][v]=true;
for(int i=0;i<4;i++){
int x=u+dx[i];
int y=v+dy[i];
if(x<0||y<0||x>=n||y>=m) continue;
if(st[x][y]||s[x][y]=='#') continue;
dfs(x,y);
}
}
int main(){
while(cin>>m>>n,n||m){
memset(st,0,sizeof st);
for(int i=0;i<n;i++)
cin>>s[i];
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(s[i][j]=='@') dfs(i,j);
cout<<ans<<endl;
ans=0;
}
}
B、Oil Deposits
题解:直接遍历字符矩阵 遇到‘@’ 并且没被标记就爆搜,每次暴搜都把搜到的点标记,这样就保证了每次搜索就是搜索一个新的连通块,这样答案就是连通块的个数=爆搜的次数,注意这里是八方向:上、下、左、右、左上、左下、右上、右下;
代码:
#include<iostream>
#include<string.h>
using namespace std;
int n,m;
int ax[8]= {-1,-1,-1,0,0,1,1,1},ay[8]= {-1,0,1,-1,1,-1,0,1};//八方向
int b[110][110];
char a[110][110];
void dfs(int u,int k)
{
b[u][k]=1;
if(u<0||u>=n||k<0||k>=m)
return ;
for(int i=0;i<8;i++){
if(a[u+ax[i]][k+ay[i]]=='@'&&b[u+ax[i]][k+ay[i]]==0)
dfs(u+ax[i],k+ay[i]);
}
}
int main()
{
//int n,m;
while(scanf("%d%d",&n,&m)&&m!=0)
{
int t=0;
memset(b,0,sizeof b);
for(int i=0; i<n; i++)
scanf("%s",&a[i]);
for(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
{
if(a[i][j]=='@'&&b[i][j]==0)//遇到@,并且之前没被搜过,这样就保证了 这次搜的是一个新的连通块
dfs(i,j),t++;
}
}
printf("%d\n",t);//答案就是连通块的个数=搜索的次数
}
}
C、Lake Counting
题解:思路同上一题,这里采用不同的搜索方式:即搜索八连通块的时候直接利用for循环遍历,把搜索过的点变成 ’ . ’ 。习惯采用那种都可以个人习惯采用B题代码
代码:
#include <iostream>
using namespace std;
int N,M;
char field[100][100];
void dfs(int x,int y){
field[x][y]='.';
for(int dx=-1;dx<=1;dx++){
for(int dy=-1;dy<=1;dy++){
int nx=x+dx,ny=y+dy;
if(0<=nx&&nx<N&&0<=ny&&ny<M&&field[nx][ny]=='W') dfs(nx,ny);
}
}
}
int main()
{
int res=0;
int i,j;
scanf("%d %d",&N,&M);
for(i=0;i<N;i++){
for(j=0;j<M;j++){
cin>>field[i][j];
}
}
for(i=0;i<N;i++){
for(j=0;j<M;j++){
if(field[i][j]=='W'){
dfs(i,j);
res++;
}
}
}
printf("%d",res);
return 0;
}
D、N皇后问题
题解:经典 n皇后问题,注意不要每次都搜,先预处理。
代码:
#include <iostream>
using namespace std;
bool st1[52], st2[52], st3[52];
int n, t, ans, a[20];
void dfs(int k) {
if(k == n) {
t++;
return;
}
for(int i = 0; i < n; i++) {
if(!st1[i] && !st2[i + k] && !st3[n - k + i] ) {
//a[k][i] = 1;
st1[i] = st2[i + k] = st3[n - k + i] = true;
dfs(k + 1);
// a[k][i] = 0;
st1[i] = st2[i + k] = st3[n - k + i] = false;
}
}
}
int main() {
for(int i = 1; i <= 10; i++) {
t = 0;
n = i;
dfs(0);
a[i] = t;
}
while(cin >> n && n != 0) {
// for(int i = 0; i < n; i++) {
// for(int j = 0; j < n; j++)
// a[i][j] = 0;
//}
//t = 0, ans = 0;
//for(int i = 0; i <= 22; i++)
// st1[i] =false, st2[i] =false, st3[i] = false;
//dfs(0);
cout << a[n] << endl;
}
}
E、排列2
题解:经典全排列问题,但是要注意可能有相同的数,所以要去重一下,还有就是0不能作为千位,最后注意处理一下输出格式,细节见代码。
代码:
#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
vector<int>p;
int b[10010];
int st[10010];
int c[30],cnt,sum;
int a[4];
void dfs(int k) {//A^A^B^C^C A^A^C^C^B
if(k==4) {//搜完了四张牌
if(b[sum]==0) {
c[cnt++]=sum;//c数组存最后的答案
b[sum]++;//b数组作为去重数组,表示sum已经出现过
}
return ;
}
for(int i=0; i<4; i++) {
if(!st[i]&&!(sum==0&&a[i]==0)) {//st数组表示第i张牌是否用过st[i]==0表示未被使用,!(sum==0&&a[i]==0)表示0不能作为最高位
st[i]++;
sum=sum*10+a[i];
dfs(k+1);
sum=sum/10;//这种情况已经搜完,要恢复现场
st[i]--;
}
}
}
int main() {
int xx=0;
while(1) {
if(xx==1)
cout<<"\n";
cnt=0,sum=0;
memset(st,0,sizeof st);
memset(b,0,sizeof b);
memset(c,0,sizeof c);
//int a[4];
cin>>a[0]>>a[1]>>a[2]>>a[3];
if(a[0]==a[1]&&a[1]==a[2]&&a[2]==a[3]&&a[0]==0)
break;
if(!(a[0]==a[1]&&a[1]==a[2]&&a[2]==a[3]&&a[0]==0)&&xx==1)
cout<<"\n";
xx=1;
memset(b,0,sizeof b);
dfs(0);
sort(c,c+cnt+1);
for(int i=1; i<=cnt; i++) {
cout<<c[i];
if(c[i]/1000!=c[i+1]/1000&&i<cnt)
cout<<endl;
else if(c[i]/1000==c[i+1]/1000)
cout<<" ";
}
}
}
F、Catch That Cow
题解:直接从给出的初始的数开始暴搜,每次转移可以转移到三种状态并且搜索的最大值不超过2e5,这里简单证明一下:因为N和K值域都是[0,1e5],所以无论给出的N和K取值多少,就算都用N++或者N-- 答案肯定不超过1e5。但是如果搜索时N超过了2e5,这个时候就只能用N–,就算是K=1e5,也要1e5步。综上,搜索时N的值肯定不会超过2e5,可以用上面的证明方式可以证明出来N也不会小于0。因为bfs搜索的时候具有最近性(求最短路时也可以表现出来),直接用bfs暴搜;
我这里用了一个小trick,因为要标记某个数是否之前被搜过,普通做法是开一个标记,我这里把 f[n]初始成1,只要 f [ i ] 不等于0,就表示之前被搜过,因为 n 这个数也要表示为被搜过所以要初始成 1 ,最后输出答案时记得 -1
代码:
#include<bits/stdc++.h>
using namespace std;
int f[200010];
int main() {
ios::sync_with_stdio(false);
int n,k;
while(cin>>n>>k) {
memset(f,0,sizeof f);
queue<int>q;
q.push(n);
f[n]=1;
while(!q.empty()) {
int t=q.front();
q.pop();
if(t+1<=2e5&&!f[t+1]) {
f[t+1]=f[t]+1;
q.push(t+1);
}
if(t-1>=0&&!f[t-1]) {
f[t-1]=f[t]+1;
q.push(t-1);
}
if(t*2<=2e5&&!f[t*2]) {
f[t*2]=f[t]+1;
q.push(t*2);
}
}
cout<<f[k]-1<<"\n";
}
}