第二次双周赛
7-1 输出全排列
题目描述
解题方法
简单的全排列问题,只需要使用简单的深搜,细节上注意回溯,则能促使每次输出为规定顺序的全排列。
解题代码
#include<iostream>
using namespace std;
int n;
int vis[20]={0};
int a[20]={0};
void dfs(int cnt)
{
if(cnt==n)//如果使用数字的个数到达n,则输出此排列
{
for(int j=1;j<=n;j++)
{
cout<<a[j];
}
cout<<endl;return;
}
for(int i=1;i<=n;i++)
{
if(vis[i]!=1)
{
vis[i]=1;//标记此数字已经使用过
cnt++;//使用数字个数+1
a[cnt]=i;//标记此位置的数字
dfs(cnt);
cnt--;//以下回溯
vis[i]=0;
}
}
return;
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
7-2 山
题目描述
解题方法
阅读完本题,明确此题为类似连通图的问题,但不是图的问题,只需要从每个点位进行搜索,在每个封闭区域经过时标记为已经过,每次能开始dfs深搜时计数器加一,就能判断有多少个连通图,得出的便是答案
解题代码
#include<iostream>
#include<cstring>
using namespace std;
int n,m,ans=0;
int vis[2020][2020],map[2020][2020];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
void dfs(int x,int y)
{
vis[x][y]=1;//标记为经过
for(int i=0;i<4;i++)//向四周搜索
{
int tx=x+dx[i];
int ty=y+dy[i];
if(tx<=0||tx>n||ty<=0||ty>m) continue;//判断越界
if(vis[tx][ty]!=1&&map[tx][ty]==1)
{
dfs(tx,ty);
}
}
return;
}
int main()
{
memset(vis,0,sizeof(vis));
memset(map,-1,sizeof(map));
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>map[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(vis[i][j]==1) continue;//如果已经过此点位,则跳过
if(map[i][j]==1)
{
ans++;//计数器+1
dfs(i,j);//从此点位开始搜索
}
}
}
cout<<ans;
return 0;
}
7-3 跳跃
题目描述
解题方法
一开始思考时,我想到的时深搜dfs,结果写完提交时发现超时,然后就换成了宽搜bfs。每到一个点位就标记经过,不用考虑什么,因为只要此地方可以到达,就不必计较从哪里来的。就感觉是简单的bfs模板题目,直接套模板就能过。
解题代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int n,flag=0;
int vis[60010],m[60010];
queue<int>q;
void bfs(int x)
{
q.push(x);
vis[x]=1;
while(!q.empty())//套bfs模板
{
int dd=q.front();//取头元素
q.pop();//出队
vis[dd]=1;
if(m[dd]==0)
{
flag=1;break;
}
int di=dd+m[dd];
if(di<n&&vis[di]==0)
{
q.push(di);//入队
}
int dj=dd-m[dd];
if(dj>=0&&vis[dj]==0)
{
q.push(dj);//同理
}
}
return;
}
int main()
{
int st;
memset(vis,0,sizeof(vis));
cin>>n;
for(int i=0;i<n;i++)
{
cin>>m[i];
}
cin>>st;
if(m[st]==0)//特殊条件
{
cout<<"True";return 0;
}
bfs(st);
if(flag) cout<<"True";//flag标记是否到达出口
else cout<<"False";
return 0;
}
7-4 最长光路
题目描述
解题方法
此问题很明显是dfs的运用,但是多了个光路的判断条件,那就按照题目的说法,规划dfs的写法。
在dfs向四周方向出发,每一条路线都要搜索一遍,然后在dfs里,增加遇到反射的情况的判断条件,然后变换方向,继续搜索。
遇到回路,则判断此方向的路线是否经过此位置,则一定为回路。
解题代码
#include<iostream>
#include<cstring>
using namespace std;
int n,m,len,ans=0;
int vis[505][505][4]={0};
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};//方向x和y要统一
char st[4]={'U','R','D','L'};//四个方向
string s[505];
char c;
void dfs(int x,int y,int k)
{
len++;
if(vis[x][y][k])//判断此方向的光路是否经过此地方
{
len=0x3fffffff;
return;
}
else vis[x][y][k]=1;
if(s[x][y]=='\\')//光路反射
{
if(st[k]=='D') k--;
else if(st[k]=='R') k++;
else if(st[k]=='U') k=3;
else if(st[k]=='L') k=0;
}
if(s[x][y]=='/')//同理
{
if(st[k]=='D') k++;
else if(st[k]=='R') k--;
else if(st[k]=='U') k++;
else if(st[k]=='L') k--;
}
int xx=x+dx[k],yy=y+dy[k];
if(xx<0||yy<0||xx>=n||yy>=m||s[xx][yy]=='C') return;//判断越界
dfs(xx,yy,k);//往此方向继续搜索
return;
}
int main()
{
int tx,ty;
cin>>n>>m;
for(int i=0;i<n;i++)
{
cin>>s[i];
}
cin>>tx>>ty;
tx--;ty--;//数组下标从0开始
for(int i=0;i<4;i++)//四个方向搜索
{
memset(vis,0,sizeof(vis));
len=0;
dfs(tx,ty,i);
if(len>ans)
{
c=st[i];
ans=len;
}
}
cout<<c<<endl;
if(ans<0x3fffffff) cout<<ans<<endl;
else cout<<"COOL"<<endl;
return 0;
}
7-5 回文数文回
题目描述
解题方法
因为此问题为回文数,可以把范围里的数直接分成两部分,一部分和另一部分一定要一样才能形成回文数。
所以直接在一部分进行每一位数枚举,另一部分反转复制,然后两部分合起来是否符合范围,那就是答案。
此为暴力枚举。
解题代码
#include<iostream>
#include<cstring>
using namespace std;
int n,ans=0,sum=0;
int a[15];
int main()
{
memset(a,0,sizeof(a));
cin>>n;
for(int i=1;i<=9;i++)//五个循环暴力枚举
{
sum=0;
a[1]=a[9]=i;
sum+=a[1]*100000000;
sum+=a[9]*1;
for(int j=0;j<=9;j++)
{
a[2]=a[8]=j;
sum+=a[2]*10000000;
sum+=a[8]*10;
for(int k=0;k<=9;k++)
{
a[3]=a[7]=k;
sum+=a[3]*1000000;
sum+=a[7]*100;
for(int q=0;q<=9;q++)
{
a[4]=a[6]=q;
sum+=a[4]*100000;
sum+=a[6]*1000;
for(int w=0;w<=9;w++)
{
a[5]=w;
sum+=a[5]*10000;
if(sum<=n) ans++;//判断是否小于最大范围
sum-=a[5]*10000;
}
sum-=a[4]*100000;
sum-=a[6]*1000;
}
sum-=a[3]*1000000;
sum-=a[7]*100;
}
sum-=a[2]*10000000;
sum-=a[8]*10;
}
sum-=a[1]*100000000;
sum-=a[9]*1;
}
cout<<ans;
return 0;
}