总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716
信息学奥赛一本通(C++版) 第二部分 基础算法 第五章 搜索与回溯算法
http://ybt.ssoier.cn:8088/
//1317 【例5.2】组合的输出
//提交,未通过,0分
//重新读题,发现“每个元素占三个字符的位置”,修改,提交,未通过,0分
//重新对输出样例进行分析,修改,提交,只对了测试点1
//转念一想,输出可能为两位数,故要采用整数对齐方式,而不是写死空格方式
//修改,提交,还只是对了测试点1,再看题目,发现r并不等于3,而写的过程中一直误解r等于3,
//还是扫了一遍https://www.cnblogs.com/agenthtb/p/5759556.html的代码才发现的
//进行了较大的修改,提交AC 2017-11-8 22:12
#include <stdio.h>
#include <string.h>
int n,r,a[30],vis[30];
void dfs(int step){
int i,j;
if(step==r+1){
for(j=1;j<=r;j++)printf("%3d",a[j]);
printf("\n");
//if(a[0]<a[1]&&a[1]<a[2])
//printf("%3d%3d%3d\n",a[0],a[1],a[2]);//此处写成printf(" %d %d %d\n",a[0],a[1],a[2]);//此处写成printf("%d %d %d\n",a[0],a[1],a[2]);//此处写成 printf("%d %d %d\n",a[0],a[1],a[2]);
return ;
}
for(i=a[step-1];i<=n;i++)//请注意i=a[step-1]技巧
if(vis[i]==0){
vis[i]=1;
a[step]=i;
dfs(step+1);
vis[i]=0;
}
}
int main(){
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&r);
a[0]=1;//请注意 a[0]=1;
dfs(1);
return 0;
}
//1318 【例5.3】自然数的拆分
//http://blog.csdn.net/u012346225/article/details/38370065此文代码写得干净利索
//边写代码,边调试,样例通过,提交,所有测试点,答案均错误,
//重新看题,发现,输出语句是没有total=14这句的
//果断删除total=这句,提交AC 2017-11-13 21:27
#include <stdio.h>
int a[100000],n;
void print(int cnt){
int i;
printf("%d=",n);
for(i=1;i<cnt;i++)//此处写成 for(i=0;i<cnt;i++)
printf("%d+",a[i]);
printf("%d\n",a[cnt]);
}
void dfs(int m,int step){
int i;
for(i=a[step-1];i<=m;i++)//此处写成 for(i=a[step-1];i<m;i++)
if(i<n){//此句功能,是去除n=n这种情况
m-=i;
a[step]=i;
if(m==0)print(step);
else dfs(m,step+1);
m+=i;
}
}
int main(){
scanf("%d",&n);
a[0]=1;
dfs(n,1);
}
//1212 LETTERS
//并且不能移向曾经经过的字母 理解花了些时间,基本弄明白,遇到雷同的字母,不在走该字母。
//对访问过的雷同字母处理,一直惴惴不安,样例通过,提交AC。2017-11-24
#include <stdio.h>
#include <string.h>
char s[25][25];
int r,c,next[][2]={{1,0},{-1,0},{0,1},{0,-1}},vis[30][30],a[30],max=0;
void dfs(int x,int y,int step){
int nr,nc,i,j,k,b;
if(step>max)max=step;
for(k=0;k<4;k++){
nr=x+next[k][0],nc=y+next[k][1];
b=s[nr][nc]-'A';
if(0<=nr&&nr<r&&0<=nc&&nc<c&&vis[nr][nc]==0&&a[b]==0){
vis[nr][nc]=1,a[b]=1;
dfs(nr,nc,step+1);
vis[nr][nc]=0,a[b]=0;
}
}
}
int main(){
int i,j;
memset(vis,0,sizeof(vis)),memset(a,0,sizeof(a));
scanf("%d%d",&r,&c);
for(i=0;i<r;i++)scanf("%s",s[i]);
vis[0][0]=1,a[s[0][0]-'A']=1;//漏了该行
dfs(0,0,1);
printf("%d",max);
return 0;
}
//1213 八皇后问题
//1214 八皇后
//洛谷 p1219 八皇后
//解法详见http://blog.csdn.net/mrcrack/article/details/61625530 洛谷 p1219 八皇后
//参考1214 八皇后 做法
//该题输出格式很是奇葩,该题的行对应1214 八皇后的列;该题的列对应1214 八皇后的行
//在输出格式上折腾了一阵后,http://blog.csdn.net/qq_36700627/article/details/78691230翻看该文才发现:
//输出格式要一致,先考虑列,再考虑行。
//提交,终于AC 2017-12-15 21:20
#include <stdio.h>
#include <string.h>
int b[10],vis[3][20],a[100][10],cnt=0;//b[i] i行对应的列值为b[i]
void dfs(int step){
int i;
if(step==8+1){
cnt++;
for(i=1;i<=8;i++)a[cnt][i]=b[i];
return;
}
for(i=1;i<=8;i++)
if(!vis[0][i]&&!vis[1][i+step]&&!vis[2][step-i+8]){
vis[0][i]=vis[1][i+step]=vis[2][step-i+8]=1;
b[step]=i;
dfs(step+1);
vis[0][i]=vis[1][i+step]=vis[2][step-i+8]=0;
}
}
int main(){
int t,i,j,k;
memset(vis,0,sizeof(vis));
dfs(1);
for(k=1;k<=cnt;k++){//此处写成 for(i=k;k<=cnt;k++)大大的笔误
printf("No. %d\n",k);
for(i=1;i<=8;i++){
for(j=1;j<=8;j++)
if(a[k][j]==i)printf("1 ");
else printf("0 ");
printf("\n");
}
}
return 0;
}
//1214 八皇后
//洛谷 p1219 八皇后
//解法详见http://blog.csdn.net/mrcrack/article/details/61625530 洛谷 p1219 八皇后
//样例通过,提交AC 2017-12-15 21:01
#include <stdio.h>
#include <string.h>
int b[10],vis[3][20],a[100][10],cnt=0;//b[i] i行对应的列值为b[i]
void dfs(int step){
int i;
if(step==8+1){
cnt++;
for(i=1;i<=8;i++)a[cnt][i]=b[i];
return;
}
for(i=1;i<=8;i++)
if(!vis[0][i]&&!vis[1][i+step]&&!vis[2][step-i+8]){
vis[0][i]=vis[1][i+step]=vis[2][step-i+8]=1;
b[step]=i;
dfs(step+1);
vis[0][i]=vis[1][i+step]=vis[2][step-i+8]=0;
}
}
int main(){
int t,i,j;
memset(vis,0,sizeof(vis));
dfs(1);
scanf("%d",&t);
while(t--){
scanf("%d",&i);
for(j=1;j<=8;j++)printf("%d",a[i][j]);
printf("\n");
}
return 0;
}
//1215 迷宫
//用dfs写怕超时,但先试试,实在不行,换bfs
//提交,所有测试点均运行超时,
//那么需要换方法了
//采用bfs方式
//样例通过,提交,只有测试点7 答案正确
//nr=q[h].r+next[k][0],nc=q[h].c+next[k][1];//此处写成 nr=r+next[k][0],nc=c+next[k][1];提交,只有测试点7 答案正确修改,提交AC
//bfs确实时间效率高于bfs太多
//2017-11-9
#include <stdio.h>
#include <string.h>
int vis[110][110],next[][2]={{-1,0},{1,0},{0,-1},{0,1}},n,er,ec;//此处写成 next[][2]={{-1,0},{1,0},{0,-1},{-1,0}}笔误,查了会
char a[110][110];
struct node{
int r,c;
}q[10100];
void bfs(int r,int c){
int i,j,k,nr,nc,h,t;
h=t=1;
q[t].r=r,q[t].c=c,t++;
while(h<t){
if(q[h].r==er&&q[h].c==ec){
printf("YES\n");
return ;
}
for(k=0;k<4;k++){//此处写成for(k=0;i<4;k++),查了好久,笔误害人啊
nr=q[h].r+next[k][0],nc=q[h].c+next[k][1];//此处写成 nr=r+next[k][0],nc=c+next[k][1];提交,只有测试点7 答案正确
if(0<=nr&&nr<n&&0<=nc&&nc<n&&a[nr][nc]=='.'&&vis[nr][nc]==0){//此处写成 if(0<=nr&&nr<=n&&0<=nc&&nc<=n&&a[nr][nc]=='.'&&vis[nr][nc]==0)边界确实有问题
vis[nr][nc]=1;
q[t].r=nr,q[t].c=nc,t++;
}
}
h++;
}
printf("NO\n");
}
int main(){
int k,i,j,sr,sc;
scanf("%d",&k);
while(k--){
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%s",a[i]);
scanf("%d%d%d%d",&sr,&sc,&er,&ec);
vis[sr][sc]=1;//漏了此句
bfs(sr,sc);
}
return 0;
}
//1216 红与黑
//采用bfs方式
//样例通过,提交AC,2017-11-9
#include <stdio.h>
#include <string.h>
char a[30][30];
int next[][2]={{1,0},{-1,0},{0,1},{0,-1}},vis[30][30],W,H;
struct node{
int r,c;
}q[500];
void bfs(int sr,int sc){
int h,t,r,c,nr,nc,i,j,cnt=0,k;
h=t=1;
q[t].r=sr,q[t].c=sc,t++;
while(h<t){
r=q[h].r,c=q[h].c,cnt++;
for(k=0;k<4;k++){
nr=r+next[k][0],nc=c+next[k][1];
if(0<=nr&&nr<H&&0<=nc&&nc<W&&a[nr][nc]=='.'&&vis[nr][nc]==0){
vis[nr][nc]=1;
q[t].r=nr,q[t].c=nc,t++;//此处写成 q[t].r=nr,q[t],c=nc,t++;查了半小时
}
}
h++;
}
printf("%d\n",cnt);
}
int main(){
int i,j,sr,sc;
while(scanf("%d%d",&W,&H)&&W&&H){
memset(vis,0,sizeof(vis));
for(i=0;i<H;i++)scanf("%s",a[i]);
for(i=0;i<H;i++)
for(j=0;j<W;j++)
if(a[i][j]=='@'){
sr=i,sc=j;
break;
}
vis[sr][sc]=1;
bfs(sr,sc);
}
return 0;
}
//1217 棋盘问题
//提交,未通过,基本与想像一致
//https://www.cnblogs.com/Asimple/p/5528323.html此文代码写得不错
//写法与初级的八皇后基本一致
//样例通过,提交AC 2017-12-2 22:01
#include <stdio.h>
#include <string.h>
char a[10][10];
int vis[10],cnt,n,k,num;//vis[]记录列
void dfs(int step){
int j;
if(num==k){//此处写成 if(num==k+1)
cnt++;
return;
}
if(step==n)return;//该功能不能放在第一次判断,应放在第二次判断,一直样例未通过的原因
for(j=0;j<n;j++)
if(a[step][j]=='#'&&vis[j]==0){//此处写成if(a[step][j]=='#'&&vis[step]==0)
vis[j]=1,num++;//此处写成 vis[step]=1,num++;
dfs(step+1);//下一行
vis[j]=0,num--;//此处写成 vis[step]=0,num--;
}
dfs(step+1);//下一行
}
int main(){
int i;
while(scanf("%d%d",&n,&k)&&!(n==-1&&k==-1)){
memset(vis,0,sizeof(vis));
for(i=0;i<n;i++)scanf("%s",a[i]);
num=0,cnt=0;
dfs(0);//此处写成dfs(1);,大失水准 ,请注意字符数组从角标0开始
printf("%d\n",cnt);
}
return 0;
}
//1218 取石子游戏
//http://blog.csdn.net/mrcrack/article/details/70112975摘抄如下:
//洛谷 P1290 欧几里德的游戏
//没什么感觉,查阅网络,总体说得太抽象。
//翻看《信息学奥赛之数学一本通》P12有详解,花了些时间看懂了。
//总归要想,没有经过一段时间训练,还真不知到怎么想。
//思路是,先模拟,找规律,模拟游戏,进行决策。
//中间遇到m/n==1不可怕,可怕的是一开始就遇到m/n==1
//最关键是首先拿到m/n>1的人,肯定赢。
//思路如下:
//若一开始m/n>1,那么先手获胜
//若一开就遇到连续的m/n==1,那么偶数次情况,先手获胜
//以下代码写得很棒,囊括了上述情况。样例通过,提交AC 2017-12-1 19:11
//这次写下来,对该算法了解更深了。
#include <stdio.h>
int main(){
int m,n,i,t,f;
while(scanf("%d%d",&m,&n)&&m&&n){
f=1;
if(m<n)t=m,m=n,n=t;
while(m/n==1){
t=m;
m=n;
n=t%n;
f=-f;
}
if(f==1)printf("win\n");
else printf("lose\n");
}
return 0;
}
//1219 马走日
//样例通过,提交AC 2017-11-15 18:21
#include <stdio.h>
#include <string.h>
int n,m,vis[15][15],cnt;
int next[][2]={{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};
void dfs(int r,int c,int s){
int k,nr,nc;
if(s==n*m){
cnt++;
return ;
}
for(k=0;k<8;k++){
nr=r+next[k][0],nc=c+next[k][1];
if(0<=nr&&nr<n&&0<=nc&&nc<m&&vis[nr][nc]==0){
vis[nr][nc]=1;
dfs(nr,nc,s+1);
vis[nr][nc]=0;
}
}
}
int main(){
int t,sr,sc;
scanf("%d",&t);
while(t--){
memset(vis,0,sizeof(vis)),cnt=0;
scanf("%d%d%d%d",&n,&m,&sr,&sc);
vis[sr][sc]=1;
dfs(sr,sc,1);
printf("%d\n",cnt);
}
}
//1220 单词接龙
2.p1019 单词接龙
http://blog.csdn.net/mrcrack/article/details/61625530
NOIP 2000 提高组 复赛 单词接龙
1.程序编写过程中,发现接龙处的判断编写有误,马上着手修改。
2.样例通过,提交40分,错了测试点1-4.
3.下载测试点1一看,傻眼了,原来有这样的测试数据:
输入:
1
envelope
e
输出:
15
4.上述测试点是挺经典的,不容易考虑到。envelope envelope 拼接成envelopenvelope
5.归根结底,还是接龙处的编写出现严重失误。
6.修改,提交,还错测试点2-4.
7.下载测试点2数据一看,更是奇葩:
输入:
2
abababab
abababc
a
输出:
19
经理解后,输出结果应如下解释:
abababab
abababab
abababc
8.应采用最小交叠,这样输出长度才最大,还是修改判断接龙处函数。
9.修改提交,错了测试点4,真是感慨,该题要考虑的情况真多啊,难不在深度优先遍历,难在接龙处判定。
10.测试点4数据也挺奇葩,接了半天龙,单词长度还不如单个的单词长。
输入:
8
no
new
name
never
national
necessary
ever
me
n
输出:
9
10.修改,提交AC。
11.该题奇难无比,不是难在深度优先遍历,而是难在接龙处的判断函数编写,真是服了,原来可以这样难。
附上AC代码,编译环境Dev-C++4.9.9.2
#include <stdio.h>
#include <string.h>
int n;
char input[60][100];
char head[10];
int vis[60];
char dragon[3000];
char cur[100];
int max=0;
int cmp(char *first,char *second){
int len1,len2,i,j,k,len;
len1=strlen(first);
len2=strlen(second);
len=len1>len2?len2:len1;
if(strcmp(first,second)==0){
i=len1-1;
j=0;
while(i<len1&&j<len2&&first[i]==second[j]){
i++;//此处写成i--查了会
j++;
}
if(j==len-1)
return 0;
}
for(k=1;k<len;k++){//k=len-1的目的是避免 邻的两部分存在包含关系;仔细一想,这个思路不行,还是要从k=len判断,因其可能完全重合,无论怎么移,都重合。
i=len1-k;
j=0;
while(i<len1&&j<len2&&first[i]==second[j]){
i++;//此处写成i--查了会
j++;
}
if(j==k)
break;
}
if(k==len)
return 0;
if(j==k)
return j;
}
void dfs(int step){
int len,i,k,j,len2;
char t[100];
for(i=1;i<=2*n;i++)
if(vis[i]==0){
k=cmp(cur,input[i]);
if(k>0){
vis[i]=1;
len=strlen(dragon);
len2=strlen(input[i]);
for(j=k;j<len2;j++)//字符串拼接
dragon[len+j-k]=input[i][j];
dragon[len+len2-k]='\0';
len2=strlen(dragon);
if(max<len2)
max=len2;
strcpy(t,cur);
strcpy(cur,input[i]);
dfs(step+1);
strcpy(cur,t);
dragon[len]='\0';
vis[i]=0;
}
}
}
int main(){
int i,j,len;
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%s",input[i]);
strcpy(input[n+i],input[i]);
}
scanf("%s",head);
for(i=1;i<=2*n;i++){
if(input[i][0]==head[0]){
strcpy(dragon,input[i]);
vis[i]=1;
len=strlen(dragon);
if(max<len)
max=strlen(dragon);//要过测试点4,此处必须对max赋值
strcpy(cur,input[i]);
dfs(1);
vis[i]=0;
}
}
printf("%d\n",max);
return 0;
}
//1221 分成互质组
//思路,找最大公约数,采用枚举的方式
//最大公约数为1,互质
//样例通过,提交测试点1,4,6,7,9答案错误
//https://www.cnblogs.com/EdSheeran/p/7630014.html看了该文代码,明白,需要将各种情况枚举,找出最小值,思路摘抄如下:
//题解:一组中的数两两互质,则任意一个数与其中两数的积互质
//依据上述思路,及文中代码进行编码
//样例通过,提交AC 2017-11-29 18:57
#include <stdio.h>
int a[20],cnt=11,n;
long long f[20];
long long gcd(long long a,long long b){//a>b;
if(b==0)return a;
return gcd(b,a%b);
}
void dfs(int k,int step){//k集合个数,step递归层次(步数)
int i;
if(step==n+1){
if(k<cnt)cnt=k;
return ;
}
for(i=1;i<=k;i++)
if(gcd(f[i],a[step])==1){//a[step]与当前集合范围内数据互质的处理
f[i]*=a[step];
dfs(k,step+1);//有可能在这个递归中,将所有元素遍历完毕
f[i]/=a[step];
}
f[k+1]*=a[step];//a[step]落在当前集合之外,开辟一个新的集合进行处理。
dfs(k+1,step+1);
f[k+1]/=a[step];
}
int main(){
int i,j,t;
scanf("%d",&n);
for(i=1;i<=n;i++)f[i]=1;
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<=n;i++)//自大到小排序 保证gcd(a,b) 中的 a>b
for(j=i+1;j<=n;j++)
if(a[i]<a[j])t=a[i],a[i]=a[j],a[j]=t;
dfs(1,1);
printf("%d",cnt);
return 0;
}
//1222 放苹果
//1206 放苹果 递归
//1192 放苹果
//http://www.cnblogs.com/dongsheng/archive/2012/08/15/2640468.html此文介绍得不错,摘抄如下:
//8 解题分析:
//9 设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n作讨论,
//10 当n>m:必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m) f(m,n) = f(m,m)
//11 当n<=m:不同的放法可以分成两类:
//12 1、有至少一个盘子空着,即相当于f(m,n) = f(m,n-1);
//13 2、所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n).
//14 而总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n)
//15 递归出口条件说明:
//16 当n=1时,所有苹果都必须放在一个盘子里,所以返回1;
//17 当没有苹果可放时,定义为1种放法;
//18 递归的两条路,第一条n会逐渐减少,终会到达出口n==1;
//19 第二条m会逐渐减少,因为n>m时,我们会return f(m,m) 所以终会到达出口m==0.
//该题可以放在阅读程序写结果
//觉得很奇怪,算法没问题,反复看题,也觉得没问题,再看未通过理由,发现格式错误
//原来是小错误啊,修改,提交AC。
#include <stdio.h>
int f(int m,int n){
if(m==0||n==1)return 1;
if(m<n)return f(m,m);
return f(m,n-1)+f(m-n,n);
}
int main(){
int m,n,i,j,k;
scanf("%d",&k);
for(i=1;i<=k;i++){
scanf("%d%d",&m,&n);
printf("%d\n",f(m,n));//此处写成printf("%d",f(m,n));
}
return 0;
}
2017-12-15 21:20 AC 该章节内容