信息学奥赛一本通(C++版) 第二部分 基础算法 第五章 搜索与回溯算法

信息学奥赛一本通(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 该章节内容

猜你喜欢

转载自blog.csdn.net/mrcrack/article/details/78484020
今日推荐