一、八皇后 Checker Challenge
假设有一张n*n表格,上面全是0代表空,然后我们一行一行的遍历,每一行放一个并做好标记
在标记的时候,实际上我们只需要标记三个方向,,左下,正下,右下(应为一行只会有一个皇后,并且已经放好皇后的行数在没有回溯的情况下不会受到接下来皇后的放置的影响)
标记是通过加一,也就是每有一个皇后的不能放的范围触及到那里,就加一,这样回溯的时候就减一,就会避免掉回溯把几个皇后的触及范围全部消掉。
#include<stdio.h>
int map[20][20]= {0},n,result[20],count=0; //统计结果的个数
int dix[]= {1,1,1}; //三方位
int diy[]= {-1,0,1};
void draw(int x,int y,int flag)//当flag=1时表示标记,为-1时表示回溯重置
{map[x][y]=map[x][y]+flag;//标记本身位置for(int i=0; i<3; i++) //定位三方位{for(int j=1; j<=n-x; j++){int a=x+dix[i]*j;int b=y+diy[i]*j;if(a>0&&a<=n)//判断x轴是否越界if(b>0&&b<=n)//判断y轴是否越界map[a][b]=map[a][b]+flag;}}
}
void run(int present)//现如今的行数
{if(present==n+1)//当每一行都放置了皇后{count++;if(count<=3){for(int i=1; i<=n; i++)printf("%d ",result[i]);printf("\n");}return ;}for(int i=1; i<=n; i++) //继续查找{if(map[present][i]==0)//当遍历到可以放置皇后的位置{result[present]=i;draw(present,i,1);//标记run(present+1);draw(present,i,-1);//回溯重置}}
}
int main()
{scanf("%d",&n);run(1);//从第一行开始选择位置printf("%d",count);return 0;
}
二、考前临时抱佛脚
我的初步想法是贪心,但是很明显不行,这个题可以将它转化为01背包问题,将一个科目总时间的一半看成一个背包,当两边脑子的时间最接近的时候就是最短的时间
#include<stdio.h>
int max(int x,int y)
{if(x>y)return x;elsereturn y;
}
int main()
{int a[10],sum=0,mm[1000]={0};for(int i=1;i<=4;i++)scanf("%d",&a[i]);for(int i=1; i<=4; i++) //一科一科的算需要多少时间{int s=0,m[100];for(int j=1; j<=a[i]; j++){scanf("%d",&m[j]);s=s+m[j];}if(a[i]==1)//当只有一道题目时,就为一道题本身{sum=sum+m[1];continue;}for(int j=1;j<=a[i];j++)for(int k=s/2;k>=m[j];k--)//取两边脑袋最相近的时候mm[k]=max(mm[k],mm[k-m[j]]+m[j]);//01背包sum=sum+s-mm[s/2];//一边脑袋最接近一半但不超过一半,另一半脑袋就是这个科目所需要的最短时间}printf("%d",sum);return 0;
}
三、马的遍历
因为没下过象棋,去网上查了一下马的走向,大概是这样的方位
用的广度搜索,深度搜索数据过大的时候时间复杂度很高
#include<stdio.h>
int n,m,endx,endy,mp[402][402],book[402][402]= {0},head=1,end=1;
int dx[8]= {-2,-1,1,2,2,1,-1,-2},dy[8]= {-1,-2,-2,-1,1,2,2,1};//马的八个方向
struct code
{int x;int y;int step;
} aa[160100];//开大一点
int main()
{scanf("%d%d%d%d",&n,&m,&endx,&endy);aa[end].x=endx;aa[end].y=endy;aa[end].step=0;end++;for(int i=1; i<=n; i++)for(int j=1; j<=m; j++)mp[i][j]=-1;book[endx][endy]=1;//标记已经走过的路while(head<end)//当无路可走的时候退出来{for(int i=0; i<8; i++)//枚举8个方向{int nextx=aa[head].x+dx[i];int nexty=aa[head].y+dy[i];if(book[nextx][nexty]==0&&nextx>0&&nextx<(n+1)&&nexty>0&&nexty<(m+1))//判断是否越界并且没有走过{book[nextx][nexty]=1;//标记此点已经走过aa[end].x=nextx;aa[end].y=nexty;aa[end].step=aa[head].step+1;end++;//队列向后延申}}head++;}for(int i=1; i<=end; i++)mp[aa[i].x][aa[i].y]=aa[i].step;for(int i=1; i<=n; i++){for(int j=1; j<=m; j++)printf("%-5d",mp[i][j]);//注意距宽printf("\n");}return 0;
}
四、PERKET
可恶啊这道题我一看就以为要用贪心,结果错了;后来我仔细分析,这道题它通过添加材料使酸度和苦度最为接近,那么在做选择的时候无非就是两种情况,一种是加了,一种是没加,所以我们只要用搜索将所有情况全部遍历出来就可以了。
像这样子,每种情况的最后要与最小值比较,取最小值
#include<stdio.h>
#include<math.h>
int chage(int x,int y)//取较小值的函数
{if(x>y)return y;elsereturn x;
}
int min=1000000000,n;
struct ppp//酸苦度结构体
{int s;int b;
} z[100];
void dfs(int i,int nows,int nowb,int book)
{if(i>n)//设立终止条件{if(book==-n)//这是一个都没选的时候return;int cha=abs(nows-nowb);min=chage(cha,min);//更新最小值return;} dfs(i+1,nows,nowb,book-1);//没选dfs(i+1,nows*z[i].s,nowb+z[i].b,book+1);//选了这个材料}
int main()
{scanf("%d",&n);for(int i=1; i<=n; i++)scanf("%d%d",&z[i].s,&z[i].b);dfs(1,1,0,0);//酸度初始值应为1,苦度初始值为0,做一个记号防止一个都没选printf("%d",min);return 0;
}
五、 迷宫
这个题和搜索的经典例题有一点不同,没有直接给出地图,出于我的知识浅薄,我在后面直接构建了一个0地图,再插入1的阻碍,再按一般套路来写就可以了
#include<stdio.h>
int sum=0,n,m,t,sx,sy,fx,fy,next[4][2]={{-1,0},{1,0},{0,-1},{0,1}},book[100][100],mp[100][100];
void dfs(int x,int y)
{int tx,ty;if(x==fx&&y==fy){sum=sum+1;//计数器return;//回到上一次递归的地方}for(int i=0;i<4;i++){tx=x+next[i][0];//下一步走向ty=y+next[i][1];if(tx>m||tx<1||ty>n||ty<1)//判断是否越界continue;if(mp[tx][ty]==0&&book[tx][ty]==0)//没有走过并且没阻碍{book[tx][ty]=1;//标记走过dfs(tx,ty);book[tx][ty]=0;//回溯}}return ;
}
typedef struct node
{int x;int y;
}point;
int main()
{point a[1000];scanf("%d%d%d",&n,&m,&t);//n为行,m为列,t为多少个障碍scanf("%d%d%d%d",&sx,&sy,&fx,&fy);//起点坐标sx,sy,终点坐标fx,fyfor(int i=1;i<=t;i++)scanf("%d%d",&a[i].x,&a[i].y);for(int i=1;i<=n;i++)//根据信息创建一个地图{for(int j=1;j<=m;j++){mp[i][j]=0;}}for(int i=1;i<=t;i++){mp[a[i].x][a[i].y]=1;//设置障碍}book[sx][sy]=1;//标记起点是已经走过的路dfs(sx,sy);printf("%d",sum);return 0;
}
六、单词方正
这个题一看就是要用搜索8个方向搜,但是我有一个奇怪的想法,我发现所有的字符串要么顺序或者逆序横着相连,要么隔着n竖向相连要么隔着n+1和n-1斜向相连就只有这八种,那么只要将二维转一维然后在特定的位置判断是不是有我特定的字母就可以了
#include<stdio.h>
int main()
{char a[1000050],b[200][200];int n,k=1;scanf("%d",&n);for(int j=1; j<=n; j++){for(int i=1; i<=n; i++){scanf(" %c",&b[j][i]);if(b[j][i]!='\n')//将二维数组转化为一维数组{a[k]=b[j][i];k++;}}}for(int i=1; i<k; i++){if(a[i]=='y')//字符串顺序相连{if(a[i+1]=='i'&&a[i+2]=='z'&&a[i+3]=='h'&&a[i+4]=='o'&&a[i+5]=='n'&&a[i+6]=='g')//字符串横向相连{for(int j=0; j<7; j++)a[i+j]-=32;}else if(a[i+n]=='i'&&a[i+2*n]=='z'&&a[i+3*n]=='h'&&a[i+4*n]=='o'&&a[i+5*n]=='n'&&a[i+6*n]=='g')//字符串竖向相连{for(int j=0; j<7; j++)a[i+n*j]-=32;}else if(a[i+(n+1)]=='i'&&a[i+2*(n+1)]=='z'&&a[i+3*(n+1)]=='h'&&a[i+4*(n+1)]=='o'&&a[i+5*(n+1)]=='n'&&a[i+6*(n+1)]=='g')//字符串斜向相连{for(int j=0; j<7; j++)a[i+(n+1)*j]-=32;}}else if(a[i]=='g')//字符串逆序相连{if(a[i+1]=='n'&&a[i+2]=='o'&&a[i+3]=='h'&&a[i+4]=='z'&&a[i+5]=='i'&&a[i+6]=='y')//字符串横向相连{for(int j=0; j<7; j++)a[i+j]-=32;}else if(a[i+n]=='n'&&a[i+2*n]=='o'&&a[i+3*n]=='h'&&a[i+4*n]=='z'&&a[i+5*n]=='i'&&a[i+6*n]=='y')//字符串竖向相连{for(int j=0; j<7; j++)a[i+n*j]-=32;}else if(a[i+(n+1)]=='n'&&a[i+2*(n+1)]=='o'&&a[i+3*(n+1)]=='h'&&a[i+4*(n+1)]=='z'&&a[i+5*(n+1)]=='i'&&a[i+6*(n+1)]=='y')//字符串斜向相连{for(int j=0; j<7; j++)a[i+(n+1)*j]-=32;}}}for(int i=1;i<k;i++){if(a[i]>=97)printf("*");elseprintf("%c",a[i]+32);if(i%n==0&&i!=n*n)printf("\n");}return 0;
}
七、 自然数的拆分问题
根据这个模板写
#include<stdio.h>
int n,a[10000];
void dfs(int m,int start,int num)
{if(m==1)//存放终止条件{for(int i=0;i<num-1;i++)printf("%d+",a[i]);printf("%d\n",a[num-1]);//最后一个数的格式return;}for(int i=start;i<m&&i<n;i++){a[num]=i;//将数一个一个存进数组m=m-i;// printf("%d\n",m);dfs(m,i,num+1);//递归m=m+i;//回溯,撤销处理结果}
}
int main()
{scanf("%d",&n);dfs(n+1,1,0);//最开始从1开始搜,在最开始一个数组的个数有0个return 0;
}
八、Lake Counting S
我们只需要遍历整个地图,只要遇到w,计数器就加一,然后将与w相连的所有w全部填上,然后再继续遍历其他的地方
#include<stdio.h>
int dx[8]= {-1,-1,-1,0,0,1,1,1},dy[8]= {-1,0,1,-1,1,-1,0,1}; //8个方向的坐标移动
int m,n,sum=0;//输入长和宽
char mp[105][105];
void dfs(int x,int y)
{mp[x][y]='.';//将水坑填上for(int j=0; j<8; j++) //8个方向{int nextx=x+dx[j];//新坐标int nexty=y+dy[j];if(nextx>0&&nextx<(m+1)&&nexty>0&&nexty<(n+1))//判断是否越界{if(mp[nextx][nexty]=='W')//判断下一步是不是水坑dfs(nextx,nexty);}}
}
int main()
{scanf("%d%d",&m,&n);//输入长和宽getchar();for(int i=1; i<=m; i++)for(int j=1; j<=n+1; j++)scanf("%c",&mp[i][j]);for(int i=1; i<=m; i++)//遍历整个地图{for(int j=1; j<=n; j++){if(mp[i][j]=='W')//当遇到水坑{sum++;//计数器加一dfs(i,j);}}}printf("%d",sum);
}
九、填涂颜色
从(1,1)开始,然后遍历每个点,如果发现这个点可以走出去,就判断此点是外围的点,然后做标记,但是遇到(1,1)就是墙这种情况,从(1,1)开始撞墙,然后导致有一部分无法遍历,所以遍历要将整个外围都遍历到
#include<stdio.h>
int n;
int mp[100][100],book[100][100];
int next[4][2]= {{0,1},{1,0},{0,-1},{-1,0}};
void dfs(int x,int y)
{int tx,ty;if(mp[x][y]==1)//等于1,说明撞墙,需要返回{return;//回到上一次递归处}mp[x][y]=3;//不等于1,说明这个点为图像外面的0,把这类点统一变成3for(int k=0; k<=3; k++) //枚举4种走法{tx=x+next[k][0];//下一步走的方位ty=y+next[k][1];if(tx>=1&&ty>=1&&tx<=n&&ty<=n&&book[tx][ty]==0)//给予边界{book[tx][ty]=1;//标记此处dfs(tx,ty);//再次递归,}}
}
int main()
{scanf("%d",&n);for(int i=1; i<=n; i++)for(int j=1; j<=n; j++)scanf("%d",&mp[i][j]);for(int i=1;i<=n;i++)//只递归(1,1)的话,就会一开始就碰壁,然后返回,所以说,需要环绕一周,这样子就可以把闭合图像周围的所有0都给标记上{dfs(1,i);dfs(n,i);dfs(i,1);dfs(i,n);}for(int i=1; i<=n; i++){for(int j=1; j<=n; j++){if(mp[i][j]==3)printf("0 ");else if(mp[i][j]==0)printf("2 ");elseprintf("%d ",mp[i][j]);}printf("\n");}
}