广搜
给定一个迷宫,求(0,0)到终点(6,5)的最短距离。
细节问题:可以通过将(x,y)处理为x*tmp+y的形式进行降维打击,这样更好向queue里存数据;另外,计算步数当然是根据前一步的步数算啦!QAQ
/*
6 5
1 1 0 1 1
1 0 1 1 1
1 0 1 0 0
1 0 1 1 1
1 1 1 0 1
1 1 1 1 1
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=405,inf=0x3f3f3f3f;
int n,m,dis[maxn][maxn],mp[maxn][maxn],tmp=100;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
queue<int>q;
void bfs(){
memset(dis,-1,sizeof(dis));
dis[1][1]=0;
int pos=tmp*1+1;
int cnt=0;
q.push(pos);
while(!q.empty()){
pos=q.front(); q.pop(); cnt++;
int nx=pos/tmp, ny=pos%tmp;
for(int i=0;i<4;++i){
int cx=nx+dx[i], cy=ny+dy[i];
if(cx<1||cy<1||cx>n||cy>m||dis[cx][cy]!=-1||!mp[cx][cy])
continue;
dis[cx][cy]=dis[nx][ny]+1;//
q.push(cx*tmp+cy);
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
cin>>mp[i][j];
bfs();
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
printf("%3d",dis[i][j]);
cout<<endl;
}
return 0;
}
马的遍历 - 洛谷
有一个 n×m 的棋盘,在某个点(x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步
细节问题:降维打击时,一定要用比数据范围略大的数进行!比如如果数最大为15,用x*5+y可能会出现x为4、y为5以及x为3、y为10的情况……QAQ
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=405,inf=0x3f3f3f3f;
int n,m,x,y,dis[maxn][maxn],mp[maxn][maxn],tmp=500;
int dx[9]={1,1,-1,-1,2,2,-2,-2};
int dy[9]={2,-2,2,-2,1,-1,1,-1};
queue<int>q;
void bfs(){
memset(dis,-1,sizeof(dis));
dis[x][y]=0;
q.push(x*tmp+y);
while(!q.empty()){
int pos=q.front(); q.pop();
int nx=pos/tmp, ny=pos%tmp;
for(int i=0;i<8;++i){
int cx=nx+dx[i], cy=ny+dy[i];
if(cx<1||cy<1||cx>n||cy>m||dis[cx][cy]!=-1)
continue;
dis[cx][cy]=dis[nx][ny]+1;
q.push(cx*tmp+cy);
}
}
}
int main(){
cin>>n>>m>>x>>y;
bfs();
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
printf("%-5d",dis[i][j]);
cout<<endl;
}
return 0;
}
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
数独挑战玩家需要根据 9×99 \times 99×9 盘面上的已知数字,推理出所有剩余位置的数字,并满足每一行、每一列、每一个粗线九宫格内的数字包含有 1-9 的数字,且不重复。
现在给你一个数独,请你解答出来。每个数独保证有且只有一个解。
细节问题:时间优化方面,可以先将未填充的格子进行记录,从而减少在深搜上花费的时间;由于小方格的位置确定比较麻烦,不防对其进行打表,这样只要将行列坐标输入即可查询所属的小方格编号。
另外,当数据量较大时,可以考虑先解决已经快确定的答案(就是那种在行或列或小方格之中几乎只需要填它一个的那种数),但sort也会导致其时间复杂度上升。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=405,inf=0x3f3f3f3f;
int h[15][15],l[15][15],cub[15][15],mp[15][15],cnt;
//int cnth[15],cntl[15],cntc[15];
struct ty{
int x,y;
int num;
}bk[90];//记录要填数字的格子,减少深搜时间
const int xiao[15][15]={{0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}};//打表,对相对应的小方格进行定位
//bool cmp(ty a,ty b){
// return a.num>b.num;
//}
void print(){
for(int i=1;i<=9;++i){
for(int j=1;j<=9;++j){
printf("%d ",mp[i][j]);
}
cout<<endl;
}
}
void dfs(int dep){
if(dep>cnt){
print();
return ;
}
for(int i=1;i<=9;++i){
int x=bk[dep].x;
int y=bk[dep].y;
if(h[x][i]||l[y][i]||cub[xiao[x][y]][i]) continue;
mp[x][y]=i; h[x][i]=1; l[y][i]=1; cub[xiao[x][y]][i]=1;
dfs(dep+1);
h[x][i]=0; l[y][i]=0; cub[xiao[x][y]][i]=0;
}
}
int main(){
for(int i=1;i<=9;++i)
for(int j=1;j<=9;++j){
scanf("%d",&mp[i][j]);
if(mp[i][j]){
h[i][mp[i][j]]=1;//第i行有数字
l[j][mp[i][j]]=1;
cub[xiao[i][j]][mp[i][j]]=1;//第方格有数字
}else{
bk[++cnt].x=i; bk[cnt].y=j;
// cnth[i]++; cntl[j]++;
// cntc[xiao[i][j]]++;
}
}
// for(int i=1;i<=cnt;++i)
// bk[i].num=max(cnth[bk[i].x],max(cntl[bk[i].y],cntc[xiao[bk[i].x][bk[i].y]]));
// sort(bk+1,bk+1+cnt,cmp);
dfs(1);
return 0;
}
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
幸运数字
定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
定义next(x)为大于等于x的第一个幸运数字。给定l,r,请求出next(l) + next(l + 1) + ... + next(r - 1) + next(r)。
第一次意识到,造有规律的数据可以用广搜!
细节问题:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1030,inf=0x3f3f3f3f;
ll l,r,ans,sav[maxn];
queue<ll>q;
void mk(){
ll sum=0,cnt=0,nw=0;
q.push(4); q.push(7);
while(nw<r){
nw=q.front(); q.pop();
sav[++cnt]=nw;
sum=nw*10+4; q.push(sum);
sum=nw*10+7; q.push(sum);
}
}
int main(){
cin>>l>>r;
mk();
int p=0;
for(int i=1;l<=r;i++){
while(p+l<=r&&sav[i]>=p+l) p++;
ans+=sav[i]*p;
l+=p; p=0;
if(l>r){
cout<<ans;
return 0;
}
}
}
链接:https://ac.nowcoder.com/acm/problem/16417
来源:牛客网[NOIP2017]奶酪
现有一块大奶酪,它的高度为 h,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系, 在坐标系中,奶酪的下表面为 z = 0,奶酪的上表面为 z = h。
现在, 奶酪的下表面有一只小老鼠 Jerry, 如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交, Jerry 则可以从奶酪下表面跑进空洞; 如果一个空洞与上表面相切或是相交, Jerry 则可以从空洞跑到奶酪上表面。位于奶酪下表面的 Jerry 想知道, 在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去?
我的想法是,边读入数据边计算任意两点之间的距离,当两个空洞相交或相切时,同时用vis数组标记两点之间是否可通,同时记录两点之中距离最高点最近的位置的坐标和最矮坐标,从最矮坐标<=0的位置进行深搜;深搜的同时标记已经走过的点。自己的想法没错,但TLE了很久,后来对比答案居然发现了新大陆!所以说耐心调自己造的bug是有好处的QAQ
回溯之后将vis还原至0是深搜中常用的操作。但为啥呢?分析了在走迷宫中它的用处以后发现,如果没有还原也不是什么致命的错误,反而还省了几行代码:
上图中若原始输入数据为0则说明迷宫有障碍,并没有在回溯后进行vis=0。但不管怎样人家还是可以遍历的呀!我们想一下什么时候非vis=0不可.想想排列问题,如果一个数字我们标记了,就说明我们不能以后再使用了,这个时候求完一个答案就必须vis=0,要不数以后就没法用了!但在走迷宫时,我们的目的是遍历整个迷宫,不管这个点有没有标记,我们都可以不断去探索其他点,vis=1相当于告诉我们不要重蹈覆辙(因为既然你还在走,而又碰到了一个以前走过的点,说明这路你之前走过却还没到目的地,所以肯定不走这条老路!),而这并不耽误你回溯到之前有其他路可选的时刻再去探索。这道奶酪题就是在走老路时给我增加了时间导致我TLE。所以说不能说到深搜就在回溯后vis=0,vis=1是在提醒你不要重蹈覆辙。
细节问题:对于开根号的问题(求距离)为了防止精度丢失就直接用它的平方判断吧!另外一定别忘了判断数据范围大数开longlong!另外我们注意到,要想在找到答案的一刻立刻退出深搜,可以通过使深搜返回int值+回溯判断的方式达成目的~
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1030,inf=0x3f3f3f3f;
struct ty{
ll x,y,z;
}p[maxn];
ll t,n,h,r,f;
int hig[maxn],low[maxn],vis[maxn],acs[maxn][maxn];
int dis(int x,int y){
return ((p[x].x - p[y].x) * (p[x].x - p[y].x)
+ (p[x].y - p[y].y) * (p[x].y - p[y].y)
+ (p[x].z - p[y].z) * (p[x].z - p[y].z) <= (4 * r * r));
}//
int dfs(int x){
if(hig[x]>=h){
f=1;
printf("Yes\n");
return 1;
}
for(int i=1;i<=n;++i){
if(vis[i]==0&&acs[x][i]){
vis[i]=1;
if(dfs(i)) return 1;
// vis[i]=0;
}
}
return 0;
}
int main(){
scanf("%lld",&t);
while(t--){
memset(acs,0,sizeof(acs));
memset(hig,0,sizeof(hig));
memset(vis,0,sizeof(vis));
scanf("%lld%lld%lld",&n,&h,&r);
f=0;
for(int i=1;i<=n;++i) low[i]=inf;
for(int i=1;i<=n;++i){
scanf("%lld%lld%lld",&p[i].x,&p[i].y,&p[i].z);
low[i]=p[i].z-r;
hig[i]=p[i].z+r;
for(int j=1;j<i;++j){
if(dis(i,j))
acs[i][j]=acs[j][i]=1;
}
}
for(int i=1;i<=n;++i){
if(low[i]<=0) {
vis[i]=1;
dfs(i);
if(f) break;
// vis[i]=0;
}
}
if(!f) printf("No\n");
}
return 0;
}
乱入:如何求两圆相交阴影部分的面积?
下图中可用海伦公式求出绿色三角形的面积,从而得出相交两点和两圆心构成的四边形面积、三角形底角大小,求出两个扇形面积后相加减去四边形的面积即可。
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
wyh的迷宫
给你一个n*m的迷宫,这个迷宫中有以下几个标识:
s代表起点;t代表终点;x代表障碍物;.代表空地
现在你们涵哥想知道能不能从起点走到终点不碰到障碍物(只能上下左右进行移动,并且不能移动到已经移动过的点)。
居然忘了有障碍这回事……我也是无语……
越界、障碍、是否重复,3个判断缺一不可!!!