做这一章时候,主要的漏洞是:
1.没有写搜素结束的条件。
2.对于如何设置搜素状态不清晰。
3.题目的意思没有解读清楚就做题
zA - Red and Black
#include<iostream>
#include<cstring>
using namespace std;
int w,h;
char mp[21][21];
bool vis[21][21];
int startx,starty;
int dx[4]={0,0,1,-1};
int dy[4]={1-1,0,0};
int length;
void dfs(int x,int y)
{
if(x<1||x>h||y<1||y>w) return ;
if(mp[x][y]=='#') return ;
mp[x][y]='#';
length++;
dfs(x-1,y);
dfs(x+1,y);
dfs(x,y+1);
dfs(x,y-1);
}
int main()
{
while(cin>>w>>h&&w!=0&&h!=0)
{
length=0;
memset(vis,false,sizeof vis);
for(int i=1;i<=h;i++)
{
for(int j=1;j<=w;j++)
{
cin>>mp[i][j];
if(mp[i][j]=='@')
{
startx=i;
starty=j;
}
}
}
dfs(startx,starty);
cout<<length<<endl;
}
return 0;
}
B - Property Distribution
#include<iostream>
#include<cstring>
using namespace std;
int w,h;
char mp[21][21];
bool vis[21][21];
int startx,starty;
int dx[4]={0,0,1,-1};
int dy[4]={1-1,0,0};
int length;
void dfs(int x,int y)
{
if(x<1||x>h||y<1||y>w) return ;
if(mp[x][y]=='#') return ;
mp[x][y]='#';
length++;
dfs(x-1,y);
dfs(x+1,y);
dfs(x,y+1);
dfs(x,y-1);
}
int main()
{
while(cin>>w>>h&&w!=0&&h!=0)
{
length=0;
memset(vis,false,sizeof vis);
for(int i=1;i<=h;i++)
{
for(int j=1;j<=w;j++)
{
cin>>mp[i][j];
if(mp[i][j]=='@')
{
startx=i;
starty=j;
}
}
}
dfs(startx,starty);
cout<<length<<endl;
}
return 0;
}
C - Ball
从这题开始受到了点启发,往左又可以分为往右,往左,层层深入,值得思考的就是用l和r记录了往左和往右的当前最值,当总共编号能到达10时,说明1-9全进去了,则结束循环。
#include<bits/stdc++.h>
using namespace std;
int h,w;
const int N=101;
bool vis[16];
int a[11],l=0,r=0,flag,n;
//i为第几个数的编号
//num记录当前已经枚举的数目。
int dfs(int i)
{
int iv;
if(i==10)
{
flag=1;
return(flag);
}
if(a[i]>l)
{
iv=l;
l=a[i];
dfs(i+1);
l=iv;
}
if(a[i]>r)
{
iv=r;
r=a[i];
dfs(i+1);
r=iv;
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
flag=0;
for(int i=0;i<10;i++) cin>>a[i];
dfs(1);
if(flag)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
D - Curling 2.0
主要这题给人启发的就是对于持续前进是如何处理的,以及对于回溯的应用还有一些其他小细节
#include<iostream>
using namespace std;
int mp[21][21];
int m,n,ex,ey,sx,sy;
int next[4][2]={0,1, 1,0, 0,-1, -1,0};
int beginx,beginy;
int finalx,finaly;
int minx;
void dfs(int x,int y,int step)
{
step++;
int dx,dy;
if(step>10) return ;
for(int i=0;i<4;i++)
{
dx=x+next[i][0];
dy=y+next[i][1];
if(mp[dx][dy]==1) continue;
while(mp[dx][dy]==0||mp[dx][dy]==2)
{
dx+=next[i][0];
dy+=next[i][1];
}
if(dx<0||dx>=n||dy<0||dy>=m) continue;
if(mp[dx][dy]==1)
{
mp[dx][dy]=0;
dfs(dx-next[i][0],dy-next[i][1],step);
mp[dx][dy]=1;//回溯
}
if(mp[dx][dy]==3&&step<minx)
{
minx=step;
continue;
}
}
}
int main()
{
int i,j;
while(scanf("%d%d",&m,&n)!=EOF)
{
if(m==0&&n==0)
break;
for(i=0;i<n;i++)
for(j=0;j<m;j++)
{
scanf("%d",&mp[i][j]);
if(mp[i][j]==2)
{
sx=i;
sy=j;
}
if(mp[i][j]==3)
{
ex=i;
ey=j;
}
}
minx=1010;
dfs(sx,sy,0);
if(minx>10)
printf("-1\n");
else
printf("%d\n",minx);
}
return 0;
}
E - Cheese
启发我bfs不是说就是仅仅只是起点和终点,也可以是一个1起点到1个终点,再以这个终点为起点到其他终点。
#include<queue>
#include<iostream>
using namespace std;
#include<algorithm>
const int INF=1e9;
const int MAX=1000+10;
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
//也可以考虑用二维方向数组解决
typedef pair<int,int> P;
char maze[MAX][MAX];
int N,M;
int sx,sy;//起点
int gx,gy;//终点
int d[MAX][MAX];
void init(){
for(int i=0;i<N;i++)
for(int j=0;j<M;j++)
d[i][j]=INF;
}
int bfs(){
queue<P>que;
que.push(P(sx,sy));
d[sx][sy]=0;
while(que.size())
{
P p=que.front();
que.pop();
if(p.first==gx&&p.second==gy) break;
for(int i=0;i<4;i++)
{
int nx=p.first+dx[i];
int ny=p.second+dy[i];
if(nx>=0&&nx<N&&ny>=0&&ny<=M&&maze[nx][ny]!='X'&&d[nx][ny]==INF)
{
que.push(P(nx,ny));
d[nx][ny]=d[p.first][p.second]+1;
}
}
}
return d[gx][gy];
}
int main(){
int nn;//从1-2-3----nn依次计算;
cin>>N>>M>>nn;
for(int i=0;i<N;i++)
scanf("%s",&maze[i]);
//找起点
for(int i=0;i<N;i++)
for(int j=0;j<M;j++)
if(maze[i][j]=='S'){
sx=i,sy=j;
}
else if(maze[i][j]>='1'&&maze[i][j]<='9')
maze[i][j]=maze[i][j]-'0';
int ans=0,cnt=1,flag=0;
for(int i=0;i<N;i++){
for(int j=0;j<M;j++){
if(maze[i][j]==cnt){
init();
gx=i,gy=j;
ans+=bfs();
sx=gx,sy=gy;
cnt++;
i=0,j=0;
if(cnt==nn+1){
flag=1;
break;
}
}
}
if(flag)
break;
}
cout<<ans<<endl;
return 0;
}
F - Meteor Shower
1. 关键在于对每个点将被摧毁时间的处理,即每个点标记的是最早被摧毁的时间。
2.当该点时间+1小于下一个移动到的点的时候就可以移动到下一个点。
3.记得初始化d[0][0]=0
4.特殊情况(0,0)一开始被轰炸也得考虑
#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
const int MAX_M = 50000;
const int MAX_N = 400 + 1;
const int INF = 100000000;
//输入
int M;
int X[MAX_M], Y[MAX_M], T[MAX_M];
int maze[MAX_N][MAX_N]; //保存地图
int d[MAX_N][MAX_N]; //保存最短步数
//4个方向
const int dx[4] = {-1, 1, 0, 0};
const int dy[4] = {0, 0, -1, 1};
int bfs()
{
if(maze[0][0]==0) return -1;
queue<P> que;
que.push(P(0,0));
d[0][0]=0;//这里没标
while(!que.empty())
{
P p=que.front();
que.pop();
int x=p.first,y=p.second;
if(maze[x][y]==INF) return d[x][y];
for(int i=0;i<4;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>=0&&yy>=0&&d[xx][yy]==INF&&d[x][y]+1<maze[xx][yy])
{
que.push(P(xx,yy));
d[xx][yy]=d[x][y]+1;
}
}
}
return -1;
}
void solve(){
//初始化地图
for(int i = 0; i < MAX_N; i ++)
fill(maze[i], maze[i] + MAX_N, INF);
//模拟轰炸场景
for(int i = 0; i < M; i ++){
maze[X[i]][Y[i]] = min(maze[X[i]][Y[i]], T[i]);
for(int j = 0; j < 4; j ++){
int nx = X[i] + dx[j], ny = Y[i] + dy[j];
if(0 <= nx && 0 <= ny)
maze[nx][ny] = min(maze[nx][ny], T[i]);
}
}
//初始化地图最小步数
for(int i = 0; i < MAX_N; i ++)
fill(d[i], d[i] + MAX_N, INF);
//宽度优先搜索
int ans = bfs();
printf("%d\n", ans);
}
int main(){
scanf("%d", &M);
for(int i = 0; i < M; i ++){
scanf("%d %d %d", &X[i], &Y[i], &T[i]);
}
solve();
return 0;
}
G - Seven Puzzle
1.新的转态记录:用map类型记录了string类型的状态以及其与初始转态的距离。
2.find()函数的应用值得学习。
3.s.erase(remove(s.begin(),s.end(),' '),s.end());的搭配应用
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const double eps = 1e-8;
const int NINF = 0xc0c0c0c0;
const int INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
const ll maxn = 1e6 + 5;
const int N = 10;
int a[N];
string s="01234567";
map<string,int>mp;
int dx[4]={1,-1,4,-4};
void bfs()
{
queue<string>q;
q.push(s);
while(!q.empty())
{
string p=q.front();
q.pop();
int pos=p.find('0');
for(int i=0;i<4;i++)
{
int tp=pos+dx[i];
if(tp>=0&&tp<=7&&(!((pos==3)&&i==0)) && (!((pos==4)&&i==1)))
{
string k=p;
swap(k[pos],k[tp]);
if(mp[k]==0 && k!="01234567"){
mp[k]=mp[p]+1;
q.push(k);
}
}
}
}
}
int main(){
bfs();
while(getline(cin,s)){
s.erase(remove(s.begin(),s.end(),' '),s.end());
printf("%d\n",mp[s]);
}
return 0;
}
H - Smallest Difference
贪心(前n/2位与后n-n/2位比较,使得两数位数相差最小)加上对stl函数 next_permutation的应用。但是注意对前导0的处理。
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
using namespace std;
int num[11];
int n;
string line;
int main()
{
cin>>n;
getchar();
while(n--)
{
getline(cin,line);
int i=0;
int ans=INF;
for(int j=0;j<line.length();j++)
{
if(line[j]!=' ')
num[i++]=(line[j]-'0');
}
if(i==2)
{
cout<<num[1]-num[0]<<endl;
continue;
}
do{
if(num[0]==0||num[i/2]==0) continue;
int a=0,b=0;
for(int j=0;j<i/2;j++) a=a*10+num[j];
for(int j=i/2;j<i;j++) b=b*10+num[j];
ans=min(ans,abs(b-a));
}while(next_permutation(num,num+i));
cout<<ans<<endl;
}
return 0;
}
I - Backward Digit Sums
也是对状态的枚举,主要是judge函数对类似于杨辉三角的处理。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<set>
#include<vector>
using namespace std;
int a[11],n,sum,b[11];
bool judge(){
int tmp;
for(int i=0;i<n-1;i++){
b[i]=a[i]+a[i+1];
}
for(int i=n-2;i>0;i--){
for(int j=0;j<i;j++){
b[j]+=b[j+1];
}
}
if(b[0]==sum)return true;
else return false;
}
int main()
{
scanf("%d%d",&n,&sum);
for(int i=0;i<n;i++){
a[i]=i+1;
}
if(n==1){
printf("1\n");
return 0;
}
do
{
if(judge()){
for(int i=0;i<n-1;i++)
{
printf("%d ",a[i]);
}
printf("%d\n",a[n-1]);
return 0;
}
}while(next_permutation(a,a+n));
return 0;
}
J - Hopscotch
暴搜,对所有转态记录去重运用了用数组储存位,然后再把值算出来放入set中。
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
int maze[6][6];
int num[6];
set<int >s;
void dfs(int r,int c,int sum)
{
if(r<1||c<1||r>5 ||c>5) return ;
if(sum==7)
{
int a=0;
for(int i=1;i<=sum;i++) a+=a*10+num[i];
s.insert(a);
return ;
}
num[sum]=maze[r][c];
dfs(r+1,c,sum+1);
dfs(r-1,c,sum+1);
dfs(r,c+1,sum+1);
dfs(r,c-1,sum+1);
}
int main()
{
memset(maze,-1,sizeof maze);
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
cin>>maze[i][j];
}
}
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
dfs(i,j,1);
}
}
cout<<s.size();
return 0;
}
K - Osenbei
关键思路在于是否想出列不需要模拟,只要对行翻转与否进行深度搜索,其中每一种转态再对每一列进行判断,由于我们需要得到最多的1,那么对于每一列,如果0多,那么就把每一列0个数记为1的个数即可实现翻转,这样处理,是可以穷尽每一种状态的最大值的。
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
int maze[15][10010];
int num[6];
int m,n;
int maxx;
void dfs(int sum)
{
if(sum==m)
{
int sum1=0;
for(int i=0;i<n;i++)
{
int sum2=0;//模拟列数操作
for(int j=0;j<m;j++)
{
if(maze[j][i]==1) sum2+=1;
}
sum1+=max(sum2,m-sum2);//翻转或不翻转
}
maxx=max(maxx,sum1);
return ;//别忘了结束
}
dfs(sum+1);//行不翻转
for(int i=0;i<n;i++)
maze[sum][i]=! maze[sum][i];
dfs(sum+1);//行翻转
}
int main()
{
while(cin>>m>>n&&m&&n)
{
maxx=0;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>maze[i][j];
}
}
dfs(0);//对行进行枚举。
cout<<maxx<<endl;
}
return 0;
}