洛谷试炼场---普及练习场

洛谷试炼场---普及练习场

简单的模拟

1.p1003 铺地毯

NOIP2011 提高组 复赛 day1 carpet 铺地毯

1.读完题目,对样例1进行模拟,进一步明白题目意图。

2.地毯数据采用结构体数组,处理起来比较方便。

3.查询点上地毯,采用自上而下方式,找到break。

4.若没有找到,输出-1

5.很快样例通过,提交AC

耗时:20分钟(从拿到题目开始计时)

难度:简单

附上AC代码,编译环境Dev-C++4.9.9.2

//2011 carpet
#include <stdio.h>
struct data{
    int a;
    int b;
    int g;
    int k;
}car[10000+10];
int main(){
    int x,y;
    int n;
    int i;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d%d%d%d",&car[i].a,&car[i].b,&car[i].g,&car[i].k);
    scanf("%d%d",&x,&y);
    for(i=n;i>=1;i--)
        if(x>=car[i].a&&x<=car[i].a+car[i].g&&y>=car[i].b&&y<=car[i].b+car[i].k)
            break;
    if(i==0)
        printf("-1\n");
    else
        printf("%d\n",i);
}

6.后记:易错地方,

6.1开了比较大的二维数组,一个不当心,内存溢出。

6.2自底向顶分析地毯,一不小心,耗时,注意,题中要求的是最靠上的地摊。for(i=n;i>=1;i--)//所以i=1效率低,应i=n才是正解。

2007-1-19

2.p1017 进制转换

NOIP2000 提高组 复赛 进制转换

1.该题难在弄懂样例,负进制,余数为>=0

2.试了一下,程序自带的/,%发现对负进制转换无用,得自个写一套。

3.弄懂了-15转-2进制,余数>=0,商正负都可以。模拟如下:

-15除-2=8余1

8除-2=-4余0

-4除-2=2余0

2除-2=-1余0

-1除-2=1余1

-15=110001

4.准备写chu,yu两个函数,对照上述模拟,写出了。

5.编码,样例很快通过,提交AC.

耗时:弄懂题意,模拟成功15分钟,编码20分钟

总耗时:35分钟

难度:中等。

附上AC代码,编译环境Dev-C++4.9.9.2

//2000 进制转换
#include <stdio.h>

int chu(int a,int b){//除的结果,a是整数,b是负进制
    int ans;
    int a1,b1;
    b1=b*(-1);
    if(a>0){//大于0
        if(a%b1==0)
            ans=a/b1*(-1);
        else{
            ans=a/b1*(-1);
        }
    }else{//小于0
        a1=a*(-1);
        if(a1%b1==0)
            ans=a1/b1;
        else{
            ans=a1/b1+1;
        }
    }
    return ans;
}
int yu(int a,int b){
    int ans;
    int a1,b1;
    b1=b*(-1);
    if(a>0){//大于0
        ans=a-chu(a,b)*b;
    }else{//小于0
        a1=a*(-1);
        ans=chu(a,b)*b1-a1;        
    }
    return ans;
}
int main(){
    int a,b;
    int a1,b1;
    int stack[100];
    int top;
    int v;
    scanf("%d%d",&a,&b);
    b1=b*(-1);
    top=-1;
    printf("%d=",a);
    while(!(a>0&&a<b1)){
        top++;
        stack[top]=yu(a,b);
        a=chu(a,b);
    }
    top++;
    stack[top]=a;
    while(top>=0){
        v=stack[top--];
        if(v<10)
            printf("%d",v);
        else{
            printf("%c",'A'+v-10);
        }
    }
    printf("(base%d)\n",b);
    return 0;
}

3.

NOIP2009 普及组 复赛 poly 多项式输出

//洛谷 p1067 多项式输出
//难度:普及-
//考点:输入,输出 ,输出格式按要求进行处理  
//适用:小学生
//陷阱:要注意的条件比较多,容易忽略:如果 x 的指数为 1,则接下来紧跟的指数部分形式为“x”;
//题目特点:样例里基本涉及大多的特殊情况,但不完备。通过该题,具有一定的题目记忆能力是必需的。
//思考:该题要注意地方有,第一项,常数项,x的一次方项,系数为0,系数为-1,系数为1,需要注意的地方有6处。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int a[100+10];
int main(){
    int n;
    int i;
    scanf("%d",&n);
    for(i=1;i<=n+1;i++)
        scanf("%d",&a[i]);
    if(a[1]!=0)//第一项处理
        if(a[1]>0)//大于0
            if(a[1]==1)
                printf("x^%d",n);
            else
                printf("%dx^%d",a[1],n);
        else//小于0
            if(a[1]==-1)
                printf("-x^%d",n);
            else
                printf("%dx^%d",a[1],n);
    for(i=2;i<=n-1;i++)//中间项处理
        if(a[i]!=0)
            if(a[i]>0)//大于0
                if(a[i]==1)
                    printf("+x^%d",n-i+1);
                else
                    printf("+%dx^%d",a[i],n-i+1);
            else//小于0
                if(a[i]==-1)
                    printf("-x^%d",n-i+1);
                else
                    printf("%dx^%d",a[i],n-i+1);
    if(a[n]!=0)//x的一次项处理
        if(a[i]>0)//大于0
                if(a[i]==1)
                    printf("+x");
                else
                    printf("+%dx",a[i]);
            else//小于0
                if(a[i]==-1)
                    printf("-x");
                else
                    printf("%dx",a[i]);
    if(a[n+1]!=0)//最后一项处理
        if(a[n+1]>0)//大于0处理
            printf("+%d\n",a[n+1]);
        else//小于0处理
            printf("%d\n",a[n+1]);
    return 0;
}

4.p1540 机器翻译

NOIP2010 提高组 复赛 translate 机器翻译

1.读题,很快弄明题意,单词不在内存中就查字典,统计查字典次数。

2.内存采用队列方式。统计进队列次数,即为查询次数。

3.程序很快编好,两个样例通过,提交20分,重新读题,发现误解题意,清空的不是第一个内存块内容,而是第一个单词内容,修改,两个样例通过,提交20分,发现,反复读题,发现内存容量用的是样例1中的3,马上修改成m,提交AC.

4.心里素质还好,此题读下来,肯定能拿100分的,所以要反复尝试,绝不能跳过,做下一道。

5.有一点要注意,队列要开1000+10。

耗时:35分钟(从拿到题目开始,其中经历两次20分,第三次AC)

附上AC代码,编译环境Dev-C++4.9.9.2

//2010 translate
#include <stdio.h>
int queue[1000+10];//队列
int main(){
    int head,tail,cur;
    int m,n,v,count;
    int i;
    scanf("%d%d",&m,&n);
    head=0;
    tail=0;
    count=0;
    for(i=0;i<n;i++){
        scanf("%d",&v);
        cur=head;//遍历队列
        while(cur!=tail){
            if(queue[cur]==v)
                break;
            cur++;
        }
        if(cur==tail){//队列中没找到
            //队列容量未到m
            if(tail-head<m){
                queue[tail]=v;
                tail++;
            }else{//队列容量已是m
                queue[tail]=v;
                tail++;
                head++;
            }
            count++;
        }else{//队列中找到
             //无需查字典,故不进行操作
        }
    }
    printf("%d\n",count);
    return 0;
}

5.NOIP2008 普及组 复赛 seat 排座椅

//洛谷 p1056 排座椅
//难度:普及/提高-
//考点:输入,输出 ,结构体,排序  
//适用:小学生
//陷阱:该题输出有顺序要求ai< ai+1 bi< bi+1,考试中读题还是需花些时间
//思路:对讲话学生进行行列分割统计,对分割线进行由大到小的排序,安排分割线。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>
struct node{
    int pos;
    int count;
}row[1000+10],col[1000+10],t;
int ans_row[1000+10],ans_col[1000+10],ans_t;
int main(){
    int m,n,k,l,d;
    int i,j;
    int a1,b1,a2,b2;
    memset(row,0,sizeof(row));
    memset(col,0,sizeof(col));
    scanf("%d%d%d%d%d",&m,&n,&k,&l,&d);
    for(i=1;i<=d;i++){
        scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
        if(b1==b2)//列相同
            if(a1>a2){
                row[a2].pos=a2;
                row[a2].count++;
            }else{
                row[a1].pos=a1;
                row[a1].count++;
            }
        if(a1==a2)//行相同
            if(b1>b2){
                col[b2].pos=b2;
                col[b2].count++;
            }else{
                col[b1].pos=b1;
                col[b1].count++;//笔误,此处写成col[b2].count++查了好半天
            }
    }
    //排序,自大到小
    for(i=1;i<=m;i++)
        for(j=i+1;j<=m;j++)
            if(row[i].count<row[j].count){
                t=row[i];
                row[i]=row[j];
                row[j]=t;
            }
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            if(col[i].count<col[j].count){
                t=col[i];
                col[i]=col[j];
                col[j]=t;
            }
    for(i=1;i<=k;i++)
        ans_row[i]=row[i].pos;
    for(i=1;i<=k;i++)
        for(j=i+1;j<=k;j++)
            if(ans_row[i]>ans_row[j]){//自小到大排序
                ans_t=ans_row[i];
                ans_row[i]=ans_row[j];
                ans_row[j]=ans_t;
            }
    for(i=1;i<=l;i++)
        ans_col[i]=col[i].pos;
    for(i=1;i<=l;i++)
        for(j=i+1;j<=l;j++)
            if(ans_col[i]>ans_col[j]){//自小到大排序
                ans_t=ans_col[i];
                ans_col[i]=ans_col[j];
                ans_col[j]=ans_t;
            }
    if(k>0){
        printf("%d",ans_row[1]);
        for(i=2;i<=k;i++)
            printf(" %d",ans_row[i]);
        printf("\n");
    }
    if(l>0){
        printf("%d",ans_col[1]);
        for(i=2;i<=l;i++)
            printf(" %d",ans_col[i]);
        printf("\n");
    }
    return 0;
}

6.p1125 笨小猴

NOIP2008 提高组 复赛 word 笨小猴

1.看完题目,是字符串操作。

2.做一个字母到整数的映射,a-0,b-1,c-2,......

3.开一个26个元素的整型数组,统计字母个数,完毕后,一次遍历,找出最大值,最小值。

4.最多次数-最小次数,对结果判定是否质数。

5.判定素数时,分三种情况讨论,0,1;2;3,4,5.....

6.代码在经历i<26时,写成了i<len,调试了3分钟。

7.样例通过后,提交AC

耗时:30分钟

难度:简单

附上AC代码,编译环境Dev-C++4.9.9.2

//2008 word 笨小猴
#include <stdio.h>
#include <string.h>
char s[100+10];
int a[26];
int main(){
    int len;
    int i,j;
    int min,max;
    int t;
    memset(a,0,sizeof(a));
    scanf("%s",s);
    len=strlen(s);
    for(i=0;i<len;i++){
        a[s[i]-'a']++;
    }
    min=999;
    max=-1;
    for(i=0;i<26;i++){//找出最小值,最大值 ,此处len,应改成26 ,查了会
        if(a[i]>0){
            if(min>a[i])
                min=a[i];
            if(max<a[i])
                max=a[i];
        }
    }
    if(min!=999&&max!=-1){//判段是否质数
        t=max-min;
        if(t<2){
            printf("No Answer\n");
            printf("0\n");
        }else if(t==2){
            printf("Lucky Word\n");
            printf("2\n");
        }else{//t>2
            for(i=2;i<t;i++)
                if(t%i==0){//非质数
                    break;
                }
            if(i==t){//质数
                printf("Lucky Word\n");
                printf("%d\n",t);
            }else{//非质数
                printf("No Answer\n");
                printf("0\n");
            }
        }
    }else{
        printf("No Answer\n");
        printf("0\n");
    }
    return 0;
}

交叉模拟

1.//p1023 税收与补贴问题
//难度:普及/提高-
//考点:输入,输出 ,整数四则运算,取整,取模  
//适用:小学生
//陷阱:程序编写过程中,工作量一大,边界上特别容易出错,特别容易出现笔误。
//思考:对样例的输出有疑惑。参考他人后才弄明白,该题弄明白样例上也可以卡住不少人。该题编写过程比较烦,考验耐心。
#include <stdio.h>
#include <stdlib.h>
struct node{
    int jia;
    int liang;
}shang[10000],shang2[10000],shang_t;

int main(){
    int yu,jia,liang,yuliang;
    int i,j,k;
    int count=0;
    int jian2;//区间2,每增加1元的减少销量
    int jian1;//区间1,每增加1元的减少销量
    int min;
    int flag;
    int sign;//1正数,-1负数。
    scanf("%d",&yu);
    while(scanf("%d%d",&jia,&liang)==2){
        if(jia==-1&&liang==-1)
            break;
        shang[count].jia=jia;
        shang[count].liang=liang;
        count++;
    }
    scanf("%d",&jian2);
    //从新生成量价区间 区间1
    j=0;
    shang2[0]=shang[0];
    for(i=1;i<count;i++){
        if(shang[i].jia-shang[i-1].jia==1){
            j++;
            shang2[j]=shang[i];
        }else{//大于1,非连续变化
            jian1=(shang[i-1].liang-shang[i].liang)/(shang[i].jia-shang[i-1].jia);
            while(shang2[j].jia<shang[i].jia){//此处 写成shang[j].jia<=shang[i].jia查了不少时间
                j++;
                shang2[j].jia=shang2[j-1].jia+1;
                shang2[j].liang=shang2[j-1].liang-jian1;
            }
        }
    }
    count=j+1;
    //区间2 生成量价区间
    if(shang2[count-1].liang>=jian2){//此处shang2[count-1].liang>jian2,漏了=造成不少麻烦
        i=count;
        shang2[i].jia=shang2[count-1].jia+1;
        shang2[i].liang=shang2[count-1].liang-jian2;
        count++;
        while(shang2[i].liang>=jian2){
            i++;
            shang2[i].jia=shang2[i-1].jia+1;
            shang2[i].liang=shang2[i-1].liang-jian2;
            count++;
        }
    }
    for(i=0;i<count;i++)
        if(shang2[i].jia==yu){
            yuliang=shang2[i].liang;
            break;
        }
    min=999999;
    for(i=-yu;i<=yu;i++){//税收补贴区间
        flag=1;
        for(j=0;j<count;j++){//商品区间
            if((yu-shang2[0].jia+i)*yuliang>=(shang2[j].jia-shang2[0].jia+i)*shang2[j].liang)
                flag*=1;
            else
                flag*=0;
        }
        if(flag==1&&min>abs(i)){
            min=abs(i);
            if(i>=0)
                sign=1;
            else
                sign=-1;
        }
    }
    if(min==999999)
        printf("NO SOLUTION\n");
    else{
        if(sign==-1)
            min*=-1;
        printf("%d\n",min);
    }  
    return 0;
}
 

2.p1031 均分纸牌

NOIP2002 提高组 复赛 均分纸牌

1.看完题,第一想法就是,算出均分后的纸牌张数,与每个位置纸牌数作差,统计负的数个数,与正的数个数,取其中最大个数,即为最少移动次数,当然此种做法估计无法AC,但猜测能拿50分左右,没有其他想法的时候,就按这个办法试试吧。
2.提交,5组数据,通过2组,得分40,还算满意。

3.仔细想了想,这个思路的问题,大方向是对的,问题在于,可能负数与整数间有间隔0,那就要多移动相应0的个数,怎么考虑有0间隔呢?

4.该题还真是难在算法,感觉有小学奥数的味道。

5.还有,一个正数补负数不够,还需要其他正数帮忙,感觉此题情况比较多啊。看开一看题目定下的策略还是不错的。

6.搜索http://www.cnblogs.com/geek-007/p/5664149.html介绍得比较好,

思路:这题是一个纯模拟题,因为牌的总张数是堆的倍数,所以排好序后每队的张数就是总张数的每堆平均数(总张数÷堆数),则只需模拟一下移动的过程即可:

①从前往后扫描数组,判断距离平均数还差几张,如果小于平均数,则用后面那张补过来,如果大于平均数,则往后补

②这题可以不用排序,从前往后模拟即可,不要看题目中给的例子,那过程和我的完全不一样而且更难理解

7.本人用的就是上述思想,提交AC.学到一招,纯模拟

难度:想不到,很难

难度:想到,简单。

附上AC代码,编译环境Dev-C++4.9.9.2

//2002 jfzp2
#include <stdio.h>
int a[100+10];
int main(){
    int n;
    int i;
    int v;
    int zcount,fcount;
    int step;
    int t;
    scanf("%d",&n);
    v=0;
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        v+=a[i];
    }
    v/=n;
    step=0;
    for(i=1;i<=n-1;i++){//直接进行模拟
        t=0;
        if(a[i]!=v){
            t=a[i]-v;
            a[i+1]+=t;
            step++;
        }
    }
    printf("%d\n",step);
    return 0;
}

附上40分代码,编译环境Dev-C++4.9.9.2

//2002 均分纸牌
#include <stdio.h>
int a[100+10];
int b[100+10];
int main(){
    int n;
    int i;
    int v;
    int zcount,fcount;
    int ans;
    scanf("%d",&n);
    v=0;
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        v+=a[i];
    }
    v/=n;
    for(i=1;i<=n;i++){
        b[i]=v-a[i];
    }
    zcount=0;
    fcount=0;
    for(i=1;i<=n;i++)
        if(b[i]>0)
            zcount++;
        else if(b[i]<0)
            fcount++;
    if(zcount>=fcount)
        ans=zcount;
    else
        ans=fcount;
    printf("%d\n",ans);    
    return 0;
}

3.p1042 乒乓球

NOIP 2003 普及组 复赛 table 乒乓球

1.字符串读取,字符统计。

2.先按正常的21球,11球,建立两套统计系统,进行处理。

3.不考虑特殊情况,先将样例通过,在进行特殊情况修改。

4.陷阱,11:0或0:11均可,即有可能赢也有可能输,这是容易忽略的地方。

5.编着编着才发现11球的含义,11:9也算一局,一直以为是总计11球,弄明白了,是有一人达到11球。

6.该题同样要注意的细节比较多。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct node{
    int w;
    int l;
}old_t[10000],new_t[10000];//old 21分 new 11分 数组100开太小
int main(){
    char s_t[30];
    int len;
    int i;
    int old_w=0,old_l=0,new_w=0,new_l=0;//初始化别忘了,一开始忘了new_w,new_l初始化,结果不对
    int flag=0;
    int old_count=0,new_count=0;
    int max;
    while(scanf("%s",s_t)){
        len=strlen(s_t);
        for(i=0;i<len;i++){
            if(s_t[i]=='W'){
                old_w++;
                new_w++;
            }
            else if(s_t[i]=='L'){
                old_l++;
                new_l++;
            }
            else if(s_t[i]=='E'){
                flag=1;
                break;
            }
            max=old_w>old_l?old_w:old_l;
            if(max>=21&&abs(old_w-old_l)>=2){//忘了绝对值,查了好一会
                old_t[old_count].w=old_w;
                old_t[old_count].l=old_l;
                old_count++;
                old_w=0;
                old_l=0;
            }
            max=new_w>new_l?new_w:new_l;
            if(max>=11&&abs(new_w-new_l)>=2){//忘了绝对值,查了好一会
                new_t[new_count].w=new_w;
                new_t[new_count].l=new_l;
                new_count++;
                new_w=0;
                new_l=0;
            }
        }
        if(flag){
            old_t[old_count].w=old_w;
            old_t[old_count].l=old_l;
            old_count++;
            
            new_t[new_count].w=new_w;
            new_t[new_count].l=new_l;
            new_count++;
            break;
        }
    }
    for(i=0;i<new_count;i++)
        printf("%d:%d\n",new_t[i].w,new_t[i].l);
    printf("\n");
    
    for(i=0;i<old_count;i++)
        printf("%d:%d\n",old_t[i].w,old_t[i].l);
}

4.//p1086 花生采摘

NOIP 2004 普及组 复赛 peanuts 花生采摘

1.借助题图,看懂题意,道路只有一条。

2.在有一定距离的两株之间跳转,原以为要用什么高深算法,模拟了一下,无论怎么跳,步数都可以归结为|r2-r1|+|c2-c1|。省去很多麻烦。

3.采用结构题存储数据,按花生数自大到小排序。

4.采摘下一株时,要判定采下下一株并且回到路边步数是否够用,若够用,采,若不够用,结束。

5.考虑包括,可能第一株都无法采摘。花生不够采。花生全部采完。三种情况。

6.程序编制过程中,处理行列颠倒了,折腾了会,但是跟踪程序还是查了出来。

7.信心满满,提交,没想到第4个测试点WA。根据提交的数据,还是行列颠倒程序修改不全造成的。if(2*nut[0].r+1>k){//此处写成 2*nut[0].c+1>k查了好半天
8.该题涉及的问题都考虑到了,出错的原因行列颠倒修改不全。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <stdlib.h>
struct node{
    int r;//行
    int c;//列
    int p;//花生数目
}nut[900],nut_t;
int main(){
    int m,n,k;
    int i,j,v;
    int count=0;
    int num=0;
    scanf("%d%d%d",&m,&n,&k);
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++){
            scanf("%d",&v);
            if(v>0){
                nut[count].r=i;
                nut[count].c=j;
                nut[count].p=v;
                count++;
            }
        }
    for(i=0;i<count;i++)//冒泡排序,自大到小
        for(j=i+1;j<count;j++)
            if(nut[i].p<nut[j].p){
                nut_t=nut[i];
                nut[i]=nut[j];
                nut[j]=nut_t;
            }
    //采摘第一株
    if(2*nut[0].r+1>k){//此处写成 2*nut[0].c+1>k查了好半天
        printf("%d\n",num);
        return 0;
    }
    else{
        k=k-nut[0].r-1;
        num+=nut[0].p;    
    }

    for(i=1;i<count;i++)
        if((abs(nut[i].r-nut[i-1].r)+abs(nut[i].c-nut[i-1].c)+1+nut[i].r)<=k){///可以采摘
             k=k-(abs(nut[i].r-nut[i-1].r)+abs(nut[i].c-nut[i-1].c)+1);
             num+=nut[i].p;
        }else//采摘不到
            break;
    printf("%d\n",num);     
    return 0;
}

5.//p1098 字符串的展开

NOIP 2007 提高组 复赛 expand 字符串的展开

1.看懂题目后,发现该题需要讨论的情况比较多。

2.第一步,先分三类,保留-号,去除-,去除-号同时添加相关字符串。

3.第三类又分为p1=3,p1=1 p1=2两种情况。

4.p1=1 p3=1;p1=1 p3=2分为字母,数字两种情况。

5.p1=2 p3=1;p2=1 p3=2分为字母,数字两种情况。

6.程序分类讨论结束。

7.编写程序,第一要素,先写最简单的情况。

8.编好一个功能,要测试一个功能。

9.为了避免低级错误,程序编写得比较慢,2个样例测试通过,提交90分,测试点6报TLE,下载测试点6进行研究,找到一组TLE数据:

2 8 2
2-4
10.经排查,发现for(j=len2;j>=1;j--)//此处写成j++,错了测试点6
11.修改,提交AC。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>
char input[100+10];
int islow(char c){
    if(c>='a'&&c<='z')//小写字母
        return 1;
    return 0;//非小写字母
}
int isnum(char c){
    if(c>='0'&&c<='9')//数字
        return 1;
    return 0;//非数字
}
int main(){
    int p1,p2,p3;
    int i,j,k,len,len2;
    scanf("%d%d%d",&p1,&p2,&p3);
    scanf("%s",input);
    len=strlen(input);
    printf("%c",input[0]);
    for(i=1;i<len;i++){//不从0开始的原因是input[0]=='-'无需处理
        if(input[i]=='-'&&i+1<len){//'-'位置输出
            if(islow(input[i-1])&&islow(input[i+1])){//字母
                if(input[i+1]-input[i-1]==1){//什么也不做,不输出  减号右边的字符恰好是左边字符的后继,只删除中间的减号
                }else if(input[i+1]<=input[i-1]){//如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号
                    printf("%c",input[i]);
                }else{
                    if(p1==3){
                        len2=(input[i+1]-input[i-1]-1)*p2;
                        for(j=0;j<len2;j++)
                            printf("*");
                    }else if(p1==1){//小写字母
                        len2=input[i+1]-input[i-1]-1;
                        if(p3==1){//顺序
                            for(j=1;j<=len2;j++)
                                for(k=1;k<=p2;k++)
                                    printf("%c",input[i-1]+j);
                        }else if(p3==2){//逆序
                            for(j=len2;j>=1;j--)
                                for(k=1;k<=p2;k++)
                                    printf("%c",input[i-1]+j);
                        }
                    }else if(p1==2){//大写字母
                        len2=input[i+1]-input[i-1]-1;
                        if(p3==1){//顺序
                            for(j=1;j<=len2;j++)
                                for(k=1;k<=p2;k++)
                                    printf("%c",input[i-1]-'a'+'A'+j);
                        }else if(p3==2){//逆序
                            for(j=len2;j>=1;j--)
                                for(k=1;k<=p2;k++)
                                    printf("%c",input[i-1]-'a'+'A'+j);
                        }
                    }
                }
            }else if(isnum(input[i-1])&&isnum(input[i+1])){//数字
                 if(input[i+1]-input[i-1]==1){//什么也不做,不输出 减号右边的字符恰好是左边字符的后继,只删除中间的减号
                }else if(input[i+1]<=input[i-1]){//如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号
                    printf("%c",input[i]);
                }else{
                    if(p1==3){
                        len2=(input[i+1]-input[i-1]-1)*p2;
                        for(j=0;j<len2;j++)
                            printf("*");
                    }else if(p3==1){//顺序
                         len2=input[i+1]-input[i-1]-1;
                         for(j=1;j<=len2;j++)
                            for(k=1;k<=p2;k++)
                                printf("%c",input[i-1]+j);
                    }else if(p3==2){//逆序
                        len2=input[i+1]-input[i-1]-1;
                         for(j=len2;j>=1;j--)//此处写成j++,错了测试点6
                            for(k=1;k<=p2;k++)
                                printf("%c",input[i-1]+j);
                    }
                }
            }else{//其它情况
                printf("%c",input[i]);
            }
        }else
            printf("%c",input[i]);
    }
    printf("\n");
    return 0;
}

2017-3-22 21:11

排序

1.

洛谷 P1177 【模板】快速排序

1.翻书,该题很容易解决,但不算掌握。

2.凭空编写,边界点的取值有些问题,等号去还是不取。

3.想了一个办法,写出一组数据进行手动模拟,弄明白了,程序再开始根据模拟进行编制。

4.很久没写快排了,如果能一次性编写成功,这次可以说快排掌握了。

5.开始动手,。

6.第一次取a[left]为中枢,得分40.

7.第二次取a[(left+right)/2]为中枢,得分60.

8.第三次取a[left],a[right],a[(left+right)/2]中间值为中枢,得分60.

9.本想上归并排序,但难度较大,时间较紧,只能作罢。

10.参考AC代码,进行模拟,发现一个很好的快排代码。

11.对着模拟,进行编码,修改小错误后,提交AC.

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int a[100000+100];
void quicksort(int left,int right){
    int i=left,j=right,t;
    int mid=a[(left+right)/2];//请注意,在处理的过程中a[(left+right)/2]的值是会变化的
    while(i<=j){//i,j的值是会跳跃的,循环结束时i>j
        while(a[i]<mid)
            i++;
        while(a[j]>mid)
            j--;
        if(i<=j){
            t=a[i];
            a[i]=a[j];
            a[j]=t;
            i++;
            j--;
        }
    }
    if(i<right)
        quicksort(i,right);
    if(left<j)
        quicksort(left,j);
    
}
int main(){
    int n;
    int i;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    quicksort(0,n-1);
    printf("%d",a[0]);
    for(i=1;i<n;i++)
        printf(" %d",a[i]);
    printf("\n");
    return 0;
}

//P1177 【模板】快速排序
//在线测评地址https://www.luogu.org/problemnew/show/P1177
//采用 归并排序
//样例通过,提交AC。甚是高兴。 没有参考任何资料
//完全是本能反应。2019-1-3
#include <stdio.h>
#define maxn 100100
int a[maxn],b[maxn],n;
void memorysort(int left,int mid,int right){//自小到大
    int i=left,j=mid+1,k=left;
    while(i<=mid&&j<=right){
        if(a[i]<a[j])b[k++]=a[i++];
        else b[k++]=a[j++];
    }
    while(i<=mid)b[k++]=a[i++];
    while(j<=right)b[k++]=a[j++];
    for(i=left;i<=right;i++)a[i]=b[i];
}
void mergesort(int left,int right){
    int mid=(left+right)/2;
    if(left==right)return;
    mergesort(left,mid);
    mergesort(mid+1,right);
    memorysort(left,mid,right);
}
int main(){
    int i;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    mergesort(1,n);
    printf("%d",a[1]);
    for(i=2;i<=n;i++)printf(" %d",a[i]);
    return 0;
}

2.p1059 明明的随机数

NOIP 2006 普及组 复赛 random 明明的随机数

1.本题考查排序,因<=100,采用写法比较简单的冒泡排序。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int main(){
    int n;
    int i,j,t;
    int a[100+10];
    int b[100+10];
    int count;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    for(i=0;i<n;i++)//冒泡排序,自小到大 
        for(j=i+1;j<n;j++)
            if(a[i]>a[j]){
                t=a[i];
                a[i]=a[j];
                a[j]=t;
            }
    count=1;
    b[0]=a[0];
    for(i=1;i<n;i++)
        if(b[count-1]!=a[i])
            b[count++]=a[i];
    printf("%d\n",count);
    printf("%d",b[0]);
    for(i=1;i<count;i++)
        printf(" %d",b[i]);
    printf("\n"); 
    return 0;
}

3.//p1068 分数线划定

NOIP 2009 普及组 复赛 score 分数线划定

1.向下取整,猜测5.5为5.问题读完,发现理解正确。

2.看了数据范围n=9000,n^2=8.1*10^7,用冒泡排序,超时可能性极大,快排出手。

3.采用结构体记录志愿者信息。

4.用快排编写不舒服,马上转向冒泡排序。

5.提交全WA,才发现测试语句未删除,删除后提交,发现错了测试点2,测试点10.

6.找到问题:

if(p[i].s>=p[q-1].s)//此处写成 if(p[i].s>=p[q].s)错了测试点2,测试点10

printf("%d %d\n",p[q-1].s,count);//此处写成 if(p[i].s>=p[q].s)错了测试点2,测试点10

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
struct node{
    int k;
    int s;
}p[10000],mid,t;

int main(){
    int n,m,q;
    int i,j;
    int count=0;
    scanf("%d%d",&n,&m);
    for(i=0;i<n;i++)
        scanf("%d%d",&p[i].k,&p[i].s);
    for(i=0;i<n;i++)
        for(j=i+1;j<n;j++)
            if(p[i].s<p[j].s){
                t=p[i];
                p[i]=p[j];
                p[j]=t;
            }else if(p[i].s==p[j].s){
                if(p[i].k>p[j].k){
                    t=p[i];
                    p[i]=p[j];
                    p[j]=t;
                }
            }
    q=m*1.5;
    for(i=0;i<n;i++)
        if(p[i].s>=p[q-1].s)//此处写成 if(p[i].s>=p[q].s)错了测试点2,测试点10
            count++;
        else
            break;
    printf("%d %d\n",p[q-1].s,count);//此处写成 if(p[i].s>=p[q].s)错了测试点2,测试点10  
    for(i=0;i<count;i++)
        printf("%d %d\n",p[i].k,p[i].s);
    return 0;
}

4.//p1781 宇宙总统
//难度:入门难度
//考点:输入,输出 ,排序,高精度算法  
//适用:小学生
//感悟:之前学过的高精度算法,比大小,如今用上了。
#include <stdio.h>
#include <string.h>
int comp(char* t,char* s){
    int slen,tlen;
    int i;
    tlen=strlen(t);
    slen=strlen(s);
    if(tlen<slen)
        return -1;//小于
    if(tlen>slen)
        return 1;//大于
    if(tlen==slen){
        for(i=0;i<tlen;i++)
            if(t[i]-s[i]>0)
                return 1;//大于
            else if(t[i]-s[i]<0)
                return -1;//小于
    }
    return 0;//等于
}
char p[30][120];
int main(){
    int n,i,j;
    char t[120];
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%s",p[i]);
    strcpy(t,p[1]);
    j=1;
    for(i=2;i<=n;i++)
        if(comp(t,p[i])<0){
            strcpy(t,p[i]);
            j=i;
        }
    printf("%d\n%s\n",j,t);
    return 0;
}

2017-3-24

排序Ex

1.//p1583 魔法照片
//难度:普及-
//考点:输入,输出 ,快速排序  
//适用:小学生
//注意:过大的数组要开到main函数的外面。
//小技巧:用结构体,记录顺序及权值,两次排序。
//思考:题目难度不大,但感觉题意挺绕的。n<=20000 n^2冒泡排序4*10^8应该超时了,求助于快速排序。
//收获:熟练编写快速排序,顺序,逆序,并且能在两值相等时,判定其他条件(分两个快排函数)。
//过程:第一次提交只有20分,八个测试点WA,明白问题出在细节处理上,重新阅读题目,对照代码, 发现快排理解有问题,快排是不稳定排序,一次只能处理一种比较情况,于是参考他人代码,决定写两个快排,每一个对应一种变量排序,问题得以解决。
#include <stdio.h>
struct node{
    int i;
    int w;
}p[20000+100],p_t;
void quicksort(int left,int right){
    int i=left,j=right;
    int mid=p[(i+j)/2].w;
    while(i<=j){
        while(p[i].w>mid)
            i++;
        while(p[j].w<mid)
            j--;
        if(i<=j){           
            p_t=p[i];
            p[i]=p[j];
            p[j]=p_t;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort(left,j);
    if(i<right)
        quicksort(i,right);
}
void quicksort2(int left,int right){
    int i=left,j=right;
    int mid=p[(i+j)/2].i;
    while(i<=j){
        while(p[i].i<mid)
            i++;
        while(p[j].i>mid)
            j--;
        if(i<=j){           
            p_t=p[i];
            p[i]=p[j];
            p[j]=p_t;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort2(left,j);
    if(i<right)
        quicksort2(i,right);
}
int main(){
    int n,k,i,j;
    int e[20];
    scanf("%d%d",&n,&k);
    for(i=1;i<=10;i++)
        scanf("%d",&e[i]);
    for(i=1;i<=n;i++){
        p[i].i=i;
        scanf("%d",&p[i].w);
    }
    quicksort(1,n);
    i=1;
    while(i<=n){
        j=1;
        while(i+1<=n&&p[i].w==p[i+1].w){
            i++;
            j++;
        }
        if(j>1)
            quicksort2(i-j+1,i);
        i++;//此处容易遗漏
    }
    
    for(i=1;i<=n;i++){
        j=(i-1)%10+1;
        p[i].w+=e[j];
    }
    quicksort(1,n);
    i=1;//此处也容易遗漏
    while(i<=n){
        j=1;
        while(i+1<=n&&p[i].w==p[i+1].w){
            i++;
            j++;
        }
        if(j>1)
            quicksort2(i-j+1,i);
        i++;
    }
    printf("%d",p[1].i);
    for(i=2;i<=k;i++)
        printf(" %d",p[i].i);
    printf("\n");
    return 0;
}
 
 

2.p1051 谁拿了最多奖学金

NOIP2005 提高组 复赛 scholar 谁拿了最多奖学金

1.读完题目,马上就决定用结构体了。

2.读取Y,N信息建议用字符串。

3.按部就班,很快将程序编好。

4.测试样例,有些小错误,静态检查,找到笔误,修改,样例通过,提交AC。

耗时:20分钟

难度:简单

附上AC代码:

//2005 scholar 谁拿了最多奖学金
#include <stdio.h>
struct student{
    char name[20+10];
    int qm_score;
    int bj_score;
    char gb[10];
    char xb[10];
    int lw;
    int jj;
}stu[100+10],stu_t;
int main(){
    int n;
    int i,j;
    int sum=0;
    scanf("%d",&n);
    for(i=0;i<n;i++){
        scanf("%s%d%d%s%s%d",&stu[i].name,&stu[i].qm_score,&stu[i].bj_score,&stu[i].gb,&stu[i].xb,&stu[i].lw);
        stu[i].jj=0;
    }
    for(i=0;i<n;i++){
        if(stu[i].qm_score>80&&stu[i].lw>=1){
            stu[i].jj+=8000;
            sum+=8000;
        }
        if(stu[i].qm_score>85&&stu[i].bj_score>80){
            stu[i].jj+=4000;
            sum+=4000;
        }
        if(stu[i].qm_score>90){
            stu[i].jj+=2000;
            sum+=2000;
        }
        if(stu[i].qm_score>85&&stu[i].xb[0]=='Y'){
            stu[i].jj+=1000;
            sum+=1000;
        }
        if(stu[i].bj_score>80&&stu[i].gb[0]=='Y'){
            stu[i].jj+=850;
            sum+=850;
        }
    }
    
    for(i=0;i<n;i++)
        for(j=i+1;j<n;j++)
            if(stu[i].jj<stu[j].jj){
                stu_t=stu[i];
                stu[i]=stu[j];
                stu[j]=stu_t;
            }
    printf("%s\n",stu[0].name);
    printf("%d\n",stu[0].jj);
    printf("%d\n",sum);
    return 0;
}

再次编写此题时,竟然提交只有30分,反复读代码,就是找不出问题,也明白肯定是读题有问题,上网查找其他途径的该题题目,还是查不出,无奈,只好搁置,第二天一早,再对照题目读代码,竟然一遍找出问题所在:

班级贡献奖,每人850元,班级评议成绩高于80分(>80)的学生干部均可获得;

代码却写成了:

班级贡献奖,每人850元,期末评议成绩高于80分(>80)的学生干部均可获得;

if(stu[i].bj>80&&stu[i].gb[0]=='Y')//好难找,必须心平气和,错误代码 if(stu[i].qm>80&&stu[i].gb[0]=='Y')

马上修改,提交AC。

2017-1-20

3.p1093 奖学金

NOIP 2007 普及组 复赛 scholar 奖学金

1.采用结构体,该题思路就会比较清晰。

2.因总数不超过300,因条件考虑较多,采用冒泡排序处理,代码处理起来比较简单。

3.按照题目要求进行程序书写,即可,注意单独写一个交换函数,可以减少代码量。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
struct node{
    int i;//序号
    int yu;//语文
    int shu;//数学
    int ying;//英语
    int zong;//总分
}stu[300+10],stu_t;
void swap(int i,int j){
    stu_t=stu[i];
    stu[i]=stu[j];
    stu[j]=stu_t;
}
int main(){
    int n;
    int i,j,yu,shu,ying;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d%d%d",&yu,&shu,&ying);
        stu[i].i=i;
        stu[i].yu=yu;
        stu[i].shu=shu;
        stu[i].ying=ying;
        stu[i].zong=yu+shu+ying;
    }
    for(i=1;i<=n;i++)//自大到小排序
        for(j=i+1;j<=n;j++)
            if(stu[i].zong<stu[j].zong)
                swap(i,j);
            else if(stu[i].zong==stu[j].zong)
                if(stu[i].yu<stu[j].yu)
                    swap(i,j);
                else if(stu[i].yu==stu[j].yu)
                    if(stu[i].i>stu[j].i)
                        swap(i,j);
    for(i=1;i<=5;i++)
        printf("%d %d\n",stu[i].i,stu[i].zong);
    return 0;
}

4.p1309 瑞士轮

NOIP 2011 普及组 复赛 swiss 瑞士轮

1.对第一轮顺序心存疑惑,反复读题,结合样例明白了,还是按:第1 名和第2 名、第 3 名和第 4名、……、第2K – 1 名和第 2K名、…… 、第2N – 1 名和第2N名,各进行一场比赛。

2.因1 ≤ N ≤ 100,000,快速排序不可避免。

3.总分相同的,约定编号较小的选手排名靠前。两个快速排序函数必须。

4.采用结构体,思维负担比较小。

5.没注意到,选手数是2*N,以为是N个选手,提交测试点7TLE,测试点8、9、10RE,马上修改,提交,测试点7、8、9、10全TLE。

6.查了查,说是要用归并排序,那么一定要掌握归并排序的时候到了。

7.弄明白了归并排序的思想,以空间换时间。写好代码,提交10分,一查,一处笔误int i,j,k,ai=1,bi=1;//笔误,此处写成bi=2查了会 ,此处笔误提交10分
8.修改,提交AC。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
struct node{
    int i;//序号
    int f;//分数
    int w;//实力
}p[200000+100],a[200000+100],b[200000+20],p_t;//此处p[100000+100]提交错了后四个测试点。
int n;
void quicksort1(int left,int right){//f自大到小排序
    int i=left,j=right;
    int mid=p[(i+j)/2].f;
    while(i<=j){
        while(p[i].f>mid)
            i++;
        while(p[j].f<mid)
            j--;
        if(i<=j){
            p_t=p[i];
            p[i]=p[j];
            p[j]=p_t;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort1(left,j);
    if(i<right)
        quicksort1(i,right);
}
void quicksort2(int left,int right){//i自小到大排序
    int i=left,j=right;
    int mid=p[(i+j)/2].i;
    while(i<=j){
        while(p[i].i<mid)
            i++;
        while(p[j].i>mid)
            j--;
        if(i<=j){
            p_t=p[i];
            p[i]=p[j];
            p[j]=p_t;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort2(left,j);
    if(i<right)
        quicksort2(i,right);
}
void mergesort(){
    int i,j,k,ai=1,bi=1;//笔误,此处写成bi=2查了会 ,此处笔误提交10分
    for(i=1;i<=2*n-1;i+=2)
        if(p[i].w>p[i+1].w){
            p[i].f+=1;
            a[ai++]=p[i];//赢
            b[bi++]=p[i+1];//输
        }else{
            p[i+1].f+=1;
            a[ai++]=p[i+1];//赢
            b[bi++]=p[i];//输
        }
//归并排序
    i=1;
    j=1;
    k=1;
    while(i<ai&&j<bi)
        if(a[i].f>b[j].f)
            p[k++]=a[i++];
        else if(a[i].f<b[j].f)
            p[k++]=b[j++];
        else if(a[i].f==b[j].f)
            if(a[i].i<b[j].i)
                p[k++]=a[i++];
            else
                p[k++]=b[j++];
    while(i<ai)
        p[k++]=a[i++];
    while(j<bi)
        p[k++]=b[j++];

}
int main(){
    int r,q;
    int i,j,k;
    scanf("%d%d%d",&n,&r,&q);
    for(i=1;i<=2*n;i++){
        p[i].i=i;
        scanf("%d",&p[i].f);
    }
    for(i=1;i<=2*n;i++)
        scanf("%d",&p[i].w);
    //第一次排序,采用快速排序    
    quicksort1(1,2*n);
    j=1;
    while(j<=2*n){//同分,按序号由小到大排序
        k=1;
        while(j+1<=2*n&&p[j].f==p[j+1].f){
            j++;
            k++;
        }
        if(k>1)
            quicksort2(j-k+1,j);
        j++;
    }
    
    //r轮,采用归并排序思想排序
    for(i=1;i<=r;i++){//r轮
        mergesort();
    }
    printf("%d\n",p[q].i);
    return 0;
}

2017-3-28

字符串处理

1.//p1603 斯诺登的密码
//难度:普及-
//考点:字符串输入,输出 ,字符串比较  
//适用:小学生
//小技巧:基本思路,将余数全部自小到大排序,之后按顺序取,即可得最小数。
//陷阱:要考虑大写字母转小写字母。
//思考:信心满满,没想到提交只通过了测试点1,注意数据可能有多个1位数,可能最终结果是0,要分别讨论。
#include <stdio.h>
#include <string.h>
void tolower(char *s){//大写转小写
    int i,len=strlen(s);
    for(i=0;i<len;i++)
        if(s[i]>='A'&&s[i]<='Z')
            s[i]=s[i]-'A'+'a';
}
char a[30][20]={"aaa","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen","twenty"};
char b[10][20]={"bbb","a","both","another"};
char c[10][20]={"ccc","first","second","third"};
char input[10][100];
int main(){
    int i,j,len,d[10],t;
    int dcount=0;
    for(i=0;i<6;i++)
        scanf("%s",input[i]);
    for(i=0;i<6;i++)
        tolower(input[i]);
    for(i=0;i<6;i++){
        for(j=1;j<=20;j++)
            if(strcmp(input[i],a[j])==0){
                d[dcount++]=j;
                break;
            }
        for(j=1;j<=3;j++)
            if(strcmp(input[i],b[j])==0){
                d[dcount++]=j;
                break;
            }
        for(j=1;j<=3;j++)
            if(strcmp(input[i],c[j])==0){
                d[dcount++]=j;
                break;
            }
    }
    
    
    
    for(i=0;i<dcount;i++)
        d[i]=d[i]*d[i]%100;
    
    for(i=0;i<dcount;i++)//自小到大排序
        for(j=i+1;j<dcount;j++)
            if(d[i]>d[j]){
                t=d[i];
                d[i]=d[j];
                d[j]=t;//此处写成 d[j]=d[i]查了会
            }
    i=0;
    while(d[i]==0)
        i++;
    if(i==dcount){//0判断
        printf("0\n");
        return 0;
    }
    if(d[i]>0&&d[i]<10)//第一个1位数
        printf("%d",d[i++]);
    for(j=i;j<dcount;j++)//下面全写成i又查了会
        if(d[j]>0&&d[j]<10)
            printf("0%d",d[j]);
        else
            printf("%d",d[j]);
    printf("\n");
    return 0;
}
 

2.p1071 潜伏者

NOIP2009 提高组 复赛 spy 潜伏者

1.很快读懂题意,想用C++里的map但忍住了。采用数学中的映射关系,'A'-0,'B'-1,'C'-2依次类推。

2.开一个密码明码对应数组,a[i]=k,i是密码,对应字母i+'A',k是明码,对应字母k+'A'。

3.初始化时,将a数组中的数值初始化为-1,因为'A'-0。

4.再次遇到a[j]时,若a[j]!=k,表明一个密码对应两个字母。

5.若count==26,表明26个字母都统计到了。

6.写代码时,小错误不断,如for循环忘记写花括号,忘记了j=s3[i]-'A',查了个半天。

7.三个样例通过后,提交90分,很是满意,考试时,应该不会为了没拿到的10分停留了,继续下一道。

耗时:30分钟。

8.搜索网络,发现http://wenku.baidu.com/link?url=mc1Mdx_N2Dy2Gld11CzsG23lmwL7FuBzKMvy39LsS7Kls6Up123QpldGbKV8X1MeQRIOLy6ARJmjtGYOfbKDICaZ2RUZ5gcfkiQhcOdfF3e与本人问题一致:(本人考试的时候想不到啊)

需要注意不僅要判斷是否每一個密文字母都存在惟一對應的明文字母,還要判斷是否每一個明文字母都存在惟一對應的密文字母。(去年我沒判斷這個,
所以測試點三WA了,九十分)

9.马上进行修改:再开了一个明码的数组b[26]。

难度:90分,简单

难度:100分,难

附上AC代码,编译环境Dev-C++4.9.9.2

//2009 syp
#include <stdio.h>
#include <string.h>
int main(){
    char s1[100+10],s2[100+10],s3[100+10];
    int a[26];//26个字母
    int b[26];
    int len1,len3;
    int i,j,k;//字母到数字的映射
    int count=0;
    scanf("%s%s%s",s1,s2,s3);
    memset(a,-1,sizeof(a));
    memset(b,-1,sizeof(b));
    len1=strlen(s1);
    len3=strlen(s3);
    for(i=0;i<len1;i++){
        j=s1[i]-'A';
        k=s2[i]-'A';
        if(a[j]==-1&&b[k]==-1){
            a[j]=k;
            b[k]=j;
            count++;//统计字母
        }else if(a[j]!=k||b[k]!=j)//已赋值
            break;
    }
    if(i==len1){//s1中每个字母都遍历了
        if(count==26){
            for(i=0;i<len3;i++){
                j=s3[i]-'A';
                printf("%c",a[j]+'A');
            }
            printf("\n");
        }else//没到26个字母
            printf("Failed\n");
    }else{
        printf("Failed\n");
    }
    return 0;
}

 

附上90分代码,编译环境Dev-C++4.9.9.2

//2009 syp
#include <stdio.h>
#include <string.h>
int main(){
    char s1[100+10],s2[100+10],s3[100+10];
    int a[26];//26个字母
    int len1,len3;
    int i,j,k;//字母到数字的映射
    int count=0;
    scanf("%s%s%s",s1,s2,s3);
    memset(a,-1,sizeof(a));
    len1=strlen(s1);
    len3=strlen(s3);
    for(i=0;i<len1;i++){
        j=s1[i]-'A';
        k=s2[i]-'A';
        if(a[j]==-1){
            a[j]=k;
            count++;//统计字母
        }else if(a[j]!=k)//已赋值
            break;
    }
    if(i==len1){//s1中每个字母都遍历了
        if(count==26){
            for(i=0;i<len3;i++){
                j=s3[i]-'A';
                printf("%c",a[j]+'A');
            }
            printf("\n");
        }else//没到26个字母
            printf("Failed\n");
    }else{
        printf("Failed\n");
    }
    return 0;
}

3.p1012 拼数

NOIP 1998 提高组 复赛 拼数

1.看了题目,发现可以按字符串读取,按字典序,由大到小排序,冒泡排序即可,字符串操作。
2.提交:竟然最后一个测试点WA。想不通啊。

3.下载了测试数据,才发现,按照目前的理解,该测试点是过不了。

4.测试数据如下:

输入:

6
321 32 407 135 13 217

输出:

4073232121713513

5.程序要大改,要根据比较的两个字符串长度进行分类讨论。

6.想到一个技巧,采用拼接的方式来决定,一个字符串是另一个字符串子串,是否交换。

321 32 32132 32321

135 13 13513 13135

7.经过修改,编了一个比较复杂的程序,提交AC.

8.翻看他人代码,怎么这么短,一读,发现正是上面6.提到的思想,马上着手修改,提交AC。

9.通过该题学到,字符串比大小比较简单的一招,如6.所述。

附上简洁代码,提交AC,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>
char input[30][30],t[30],t2[100],t3[100];
void swap(int i,int j){
    strcpy(t,input[i]);
    strcpy(input[i],input[j]);
    strcpy(input[j],t);
}
int main(){
    int n,i,j,k,s,leni,lenj;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%s",input[i]);
    for(i=0;i<n;i++)//按字典序,由大到小排序
        for(j=i+1;j<n;j++){
            strcpy(t2,input[i]);
            strcat(t2,input[j]);
            strcpy(t3,input[j]);
            strcat(t3,input[i]);
            if(strcmp(t2,t3)<0)
                swap(i,j);
        }
    for(i=0;i<n;i++)
        printf("%s",input[i]);
    printf("\n");
    return 0;
}

 

附上编写比较复杂,提交AC的代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>
char input[30][30],t[30],t2[100],t3[100];
void swap(int i,int j){
    strcpy(t,input[i]);
    strcpy(input[i],input[j]);
    strcpy(input[j],t);
}
int main(){
    int n,i,j,k,s,leni,lenj;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%s",input[i]);
    for(i=0;i<n;i++){//按字典序,由大到小排序
        leni=strlen(input[i]);
        for(j=i+1;j<n;j++){
            lenj=strlen(input[j]);
            if(leni==lenj){
                if(strcmp(input[i],input[j])<0){
                    swap(i,j);
                }
            }else if(leni<lenj){
                if(strcmp(input[i],input[j])>0){//什么事也不干
                }else if(strcmp(input[i],input[j])<0){
                    k=0;
                    s=0;
                    while(k<leni&&input[i][k]==input[j][s]){
                        k++;
                        s++;
                    }
                    if(k==leni){//什么事情也不干
                         strcpy(t2,input[i]);
                         strcat(t2,input[j]);
                         strcpy(t3,input[j]);
                         strcat(t3,input[i]);
                         if(strcmp(t2,t3)<0){
                            swap(i,j);
                         }
                    }else{
                        swap(i,j);
                    }
                }
            }else if(leni>lenj){
                if(strcmp(input[i],input[j])<0){
                    swap(i,j);
                }else if(strcmp(input[i],input[j])>0){
                    k=0;
                    s=0;
                    while(s<lenj&&input[i][k]==input[j][s]){
                        k++;
                        s++;
                    }
                    if(s==lenj){
                        strcpy(t2,input[i]);
                        strcat(t2,input[j]);
                        strcpy(t3,input[j]);
                        strcat(t3,input[i]);
                        if(strcmp(t2,t3)<0){
                            swap(i,j);
                        }
                    }else{//什么事也不干
                    }
                }
            }
        }
    }
    for(i=0;i<n;i++)
        printf("%s",input[i]);
    printf("\n");
    return 0;
}

 

附上75分代码,编译环境Dev-C++4.9.9.

#include <stdio.h>
#include <string.h>
int main(){
    int n,i,j;
    char input[30][30],t[30];
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%s",input[i]);
    for(i=0;i<n;i++)//按字典序,由大到小排序
        for(j=i+1;j<n;j++)
            if(strcmp(input[i],input[j])<0){
                strcpy(t,input[i]);
                strcpy(input[i],input[j]);
                strcpy(input[j],t);
            }
    for(i=0;i<n;i++)
        printf("%s",input[i]);
    printf("\n");
    return 0;
}

4.//p1538 迎春舞会之数字舞蹈
//难度:普及/提高-
//考点:输入,输出 ,字符串处理  
//适用:小学生
//陷阱:该题数据要求十分严格,第一个数字之前不能出现空格,最后一个数字后不能出现空格。
//思考:读题,感觉有些读不懂,看样例的输入输出,发现是组成象形的数字,k为每个数字占有的空间大小。
//疑惑:对k有疑问,大小是指横向,是指纵向,还是指横向和纵向?按照纵向横向一齐扩大来处理,提交AC。
//技巧:画图进行模拟,决定以8为蓝本,进行处理,因程序打印是逐行处理,故分为5行。写得过程中,发现只需编写画横线,画竖线的函数即可。
//提示:建议大家直接输出,不要保存。很明显,用数组保存,容量不够。
#include <stdio.h>
#include <string.h>
//0,1,2,3,4,5,6,7,8,9模拟
int a[10][7]={{1,3,0,3,1},{0,2,0,2,0},{1,2,1,1,1},{1,2,1,2,1},{0,3,1,2,0},{1,1,1,2,1},{1,1,1,3,1},{1,2,0,2,0},{1,3,1,3,1},{1,3,1,2,1}};
char s[300];
int k,len;
void drawh(int line){//画横线
    int j,m,d;
    for(j=0;j<len;j++){//第一行
        if(j>0)
            printf(" ");//数字前的空格
        d=s[j]-'0';
        printf(" ");
        for(m=1;m<=k;m++)
            if(a[d][line])
                printf("-");
            else
                printf(" ");
        printf(" ");
    }
    printf("\n");
}
void drawv(int line){//画竖线
    int i,j,m,d;
    for(i=1;i<=k;i++){//第二行
        for(j=0;j<len;j++){
            d=s[j]-'0';
            if(j>0)
                printf(" ");
            if(a[d][line]==0){//无竖线
                for(m=1;m<=k+2;m++)
                    printf(" ");
            }else if(a[d][line]==1){//左竖线
                printf("|");
                for(m=1;m<=k+1;m++)
                    printf(" ");
            }else if(a[d][line]==2){//右竖线
                for(m=1;m<=k+1;m++)
                    printf(" ");
                printf("|");
            }else if(a[d][line]==3){//左右竖线
                printf("|");
                for(m=1;m<=k;m++)
                    printf(" ");
                printf("|");
            }
        }
        printf("\n");
    }
}
int main(){
    scanf("%d",&k);
    scanf("%s",s);
    len=strlen(s);
    drawh(0);
    drawv(1);
    drawh(2);
    drawv(3);
    drawh(4);
    return 0;
}

通过该组,明显感觉字符串处理上了一个台阶,学会了字符串的冒泡排序,学会了如何绘制一个比较复杂的图形。

2017-3-30

贪心

1.p1090 合并果子

NOIP 2004 提高组 复赛 fruit 合并果子

1.读完题,马上意识到,该问题就是一棵哈夫曼树(赫夫曼树)。

2.刚好学过这个算法,但还没有编过码,感觉是手到擒来。

3.看了n的范围,冒泡排序会超时,采用快速排序。采用自大到小方式,逐步缩小数据长度。

4.提交40分,测试点5-10全部超时。

5.要改进算法。想想也是算法复杂度O(n*n*logn)不超时才怪。

6.上网搜索,发现堆的概念,值得一学。

7.堆排序需要消化,那先采用快速排序与插入排序。

附上采用快速排序与插入排序,提交AC的代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int a[10000+100];
void quicksort(int left,int right){
    int i=left,j=right,t;
    int mid=a[(i+j)/2];
    while(i<=j){
        while(a[i]>mid)
            i++;
        while(a[j]<mid)
            j--;//此处写成j++笔误
        if(i<=j){
            t=a[i];
            a[i]=a[j];
            a[j]=t;
            i++;
            j--;
        }
    }
    if(left<j)//出处漏写
        quicksort(left,j);
    if(i<right)//此处漏写
        quicksort(i,right);
}

int main(){
    int n,i,j,ans=0,t;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    quicksort(1,n);
    while(1){
        t=a[n-1]+a[n];
        ans=ans+t;
        if(n==2)
            break;
        if(a[n-2]>=t)//插入排序
            a[n-1]=t;
        else{
            j=n-2;
            while(j>=1&&a[j]<t)//此处漏写j>=1查了会,只对了测试点1
                j--;
            for(i=n-1;i>j+1;i--)
                a[i]=a[i-1];
            a[i]=t;
        }
        n--;
    }
    printf("%d\n",ans);
    return 0;
}

2.//p1181 数列分段Section I
//难度:入门难度
//考点:输入,输出 ,贪心算法  
//适用:小学生
//注意:循环结束时要对最后一个区间进行判定。
//思路:自左向右扫描,按顺序每次找和尽可能接近m的数据。
#include <stdio.h>
int a[100000+100];
int main(){
    int n,m;
    int i,j;
    int ans=0,count=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    i=1;
    while(i<=n){
        if(ans+a[i]<=m){
            ans+=a[i];
            i++;
        }else{//大于m
             count++;
             ans=0;
        }
    }
    //最后一个区间判定
    if(ans<=m)
        count++;
    printf("%d\n",count);
    return 0;
}
 

3.//p1208 [USACO1.3]混合牛奶 Mixing Milk
//难度:普及-
//考点:输入,输出 ,贪心算法 ,快速排序
//适用:小学生
//思路:算单价,单价便宜的用得最多,注意边界处理。 因5000*5000用冒泡排序容易超时,故采用快速排序
#include <stdio.h>
int p[5000+100],a[5000+100];
void quicksort(int left,int right){//自小到大排序
    int i=left,j=right;
    int mid=p[(i+j)/2],t;
    while(i<=j){
        while(p[i]<mid)
            i++;
        while(p[j]>mid)
            j--;
        if(i<=j){
            t=p[i];
            p[i]=p[j];
            p[j]=t;
            t=a[i];
            a[i]=a[j];
            a[j]=t;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort(left,j);
    if(i<right)
        quicksort(i,right);
}
int main(){
    int n,m;
    int i,j;
    int money=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&p[i],&a[i]);
    quicksort(1,m);
    for(i=1;i<=m;i++)
        if(n-a[i]>=0){
            n-=a[i];
            money+=p[i]*a[i];
        }else
            break;
    money+=n*p[i];
    printf("%d\n",money);
    return 0;
}
 

4.//p1223 排队接水
//难度:普及-
//考点:输入,输出 ,冒泡排序,快速排序均可  
//适用:小学生
//思考:对样例进行模拟,发现输出3 2 7 8 1 4 9 6 10 5对应1 12 33 55 56 99 99 234 812 1000马上找到该题做法。
//提交:错了测试点8,测试点10,经对比,发现,求和需采用long long sum;
//反思:快速排序是不稳定排序,为了更加准确,准备用冒泡排序重写快速排序部分。
#include <stdio.h>
struct node{
    int t;
    int i;
}a[1000+100],a_t;
int main(){
    int n,i,j,ans;
    long long sum=0;//之前int sum=0;错了测试点8 测试点10 题目有问题,说好的10^3*10^6消失了
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i].t);
        a[i].i=i;
    }
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            if(a[i].t>a[j].t){
                a_t=a[i];
                a[i]=a[j];
                a[j]=a_t;
            }
    printf("%d",a[1].i);
    for(i=2;i<=n;i++)
        printf(" %d",a[i].i);
    printf("\n");
    for(i=1;i<=n;i++)
        for(j=1;j<=i-1;j++)
            sum+=a[j].t;
    printf("%.2f",sum/(n*1.0));
    return 0;
}
 

5.p1094 纪念品分组

NOIP 2007 普及组 复赛 group 纪念品分组

1.n<=30000快速排序是免不了了。

2.最大与最小组合,若不行,则最大单独一组,如此重复。贪心算法

3.写法有些类似快速排序,一次性提交AC.

4.注意i==j时,只剩一个数据,也算一组。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int p[30000+100];
void quicksort(int left,int right){//自小到大排序
    int i=left,j=right,t;
    int mid=p[(i+j)/2];
    while(i<=j){
        while(p[i]<mid)
            i++;
        while(p[j]>mid)
            j--;
        if(i<=j){
            t=p[i];
            p[i]=p[j];
            p[j]=t;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort(left,j);
    if(i<right)
        quicksort(i,right);
}
int main(){
    int w,n,i,j;//i标记排好序的左边,j标记排好序的右边
    int count=0;
    scanf("%d%d",&w,&n);
    for(i=1;i<=n;i++)
        scanf("%d",&p[i]);
    quicksort(1,n);
    i=1;
    j=n;
    while(i<j){
        if(p[j]+p[i]<=w){
            j--;
            i++;
            count++;
        }else{
            j--;
            count++;
        }
    }
    if(i==j)
        count++;
    printf("%d\n",count);
    return 0;
}

6.//p1803 凌乱的yyy
//难度:普及-
//考点:输入,输出 ,快速排序 ,贪心算法
//适用:小学生
//思路:按照最早结束时间进行排序,
#include <stdio.h>
int a[1000000+100],b[1000000+100];
void quicksort(int left,int right){//自小到大,最早结束时间
    int i=left,j=right,t;
    int mid=b[(i+j)/2];
    while(i<=j){
        while(b[i]<mid)
            i++;
        while(b[j]>mid)
            j--;
        if(i<=j){
            t=b[i];
            b[i]=b[j];
            b[j]=t;
            t=a[i];
            a[i]=a[j];
            a[j]=t;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort(left,j);
    if(i<right)
        quicksort(i,right);
}
int main(){
    int n,i,j,count;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d%d",&a[i],&b[i]);
    quicksort(1,n);
    count=1;
    i=1;
    j=2;
    while(j<=n){
        if(b[i]<=a[j]){
            i=j;
            j++;
            count++;
        }else
            j++;
    }
    printf("%d\n",count);
    return 0;
}
2017-3-30 21:40

深度优先搜索

1.

洛谷 p1219 八皇后

1.深度优先算法应是手到擒来,上手编码,测试通过,提交,87分,最有一个测试点TLE.

附上代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int n;
int a[20];
int visited[20];
int count=0;
void dfs(int step){
    int i,j,flag;
    if(step==n+1){
        count++;
        if(count<=3){
            for(i=1;i<=n;i++)
                printf("%d ",a[i]);
            printf("\n");
        }
        return;
    }
    for(i=1;i<=n;i++){
        if(visited[i]==0){
            flag=0;
            for(j=1;j<step;j++)
                if(abs(a[j]-i)==abs(j-step)){
                    flag=1;
                    break;
                }
            if(flag==0){
                a[step]=i;
                visited[i]=1;
                dfs(step+1);
                visited[i]=0;
            }
        }
    }
}
int main(){
    memset(visited,0,sizeof(visited));
    scanf("%d",&n);
    dfs(1);
    printf("%d\n",count);
    return 0;
}
2.翻开《算法竞赛入门经典(第2版)》,进行优化,提交87分,最后一个测试点TLE.

附上代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int n;
int a[20];
int visited[3][30];
int count=0;
void dfs(int step){
    int i,j,flag;
    if(step==n+1){
        count++;
        if(count<=3){
            for(i=1;i<=n;i++)
                printf("%d ",a[i]);
            printf("\n");
        }
        return;
    }
    for(i=1;i<=n;i++){
        if(!visited[0][i]&&!visited[1][i+step]&&!visited[2][step-i+n]){
            visited[0][i]=visited[1][i+step]=visited[2][step-i+n]=1;
            a[step]=i;
            dfs(step+1);
            visited[0][i]=visited[1][i+step]=visited[2][step-i+n]=0;
        }
    }
}
int main(){
    memset(visited,0,sizeof(visited));
    scanf("%d",&n);
    dfs(1);
    printf("%d\n",count);
    return 0;
}

3.程序就此搁置,有半个月了,突然想到,参考参考他人AC代码,发现全跟自己第2个代码一样,只是本人用的二维数组,他人用的是一维数组。马上进行修改,还是最后一个测试点TLE。

4.仔细对比差别,第二个代码

int a[20];
int visited[3][30];

改成

int a[100];
int visited[3][100];

提交AC,天啊,折腾了这么长时间。

5.大家注意,数组不要开得太抠,开得大一些,能避免很多意想不到的问题。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int n;
int a[100];
int visited[3][100];
int count=0;
void dfs(int step){
    int i,j,flag;
    if(step==n+1){
        count++;
        if(count<=3){
            for(i=1;i<=n;i++)
                printf("%d ",a[i]);
            printf("\n");
        }
        return;
    }
    for(i=1;i<=n;i++){
        if(!visited[0][i]&&!visited[1][i+step]&&!visited[2][step-i+n]){
            visited[0][i]=visited[1][i+step]=visited[2][step-i+n]=1;
            a[step]=i;
            dfs(step+1);
            visited[0][i]=visited[1][i+step]=visited[2][step-i+n]=0;
        }
    }
}
int main(){
    memset(visited,0,sizeof(visited));
    scanf("%d",&n);
    dfs(1);
    printf("%d\n",count);
    return 0;
}


2017-3-26 9:46

2.p1019 单词接龙

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;
}

2017-3-31 22:05

3.//p1101 单词方阵
//难度:普及-
//考点:输入,输出 ,字符串,深度优先遍历  
//适用:小学生
//过程:编写过程中,忘记对字符矩阵进行读取,输出结果不对,查了个半天。
//收获:写深度优先遍历的水平更进一步。
#include <stdio.h>
#include <string.h>
char input[100+10][100+10];
int vis[100+10][100+10];//访问标识
int book[100+10][100+10];//标记二维数组
int n;
char yz[10]="yizhong";
int len;
int next[][2]={{1,0},{-1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};//上下左右 左上 右上 左下 右下
void dfs(int step,int row,int col,int dir){//dir朝某一固定方向
    int i,j;
    int nr,nc;
    
    if(step==len){
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                if(vis[i][j]==1)
                    book[i][j]=1;
        return;
    }
    nr=row+next[dir][0];
    nc=col+next[dir][1];
    
    if(nr>=0&&nr<n&&nc>=0&&nc<n&&vis[nr][nc]==0&&input[nr][nc]==yz[step]){
        
        vis[nr][nc]=1;
        dfs(step+1,nr,nc,dir);
        vis[nr][nc]=0;
    }
}
int main(){
    int i,j,k;
    len=strlen(yz);
    memset(vis,0,sizeof(vis));
    memset(book,0,sizeof(book));
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%s",input[i]);
    for(i=0;i<n;i++)
        for(j=0;j<n;j++){
            if(input[i][j]==yz[0])
                for(k=0;k<8;k++){
                    vis[i][j]=1;
                    dfs(1,i,j,k);
                    vis[i][j]=0;
                }
        }
    for(i=0;i<n;i++){
        for(j=0;j<n;j++)
            if(book[i][j]==0)
                printf("*");
            else
                printf("%c",input[i][j]);
        printf("\n");
    }
    return 0;
}
 

4.//p1605 迷宫
//难度:普及-
//考点:输入,输出 ,二维数组,深度优先遍历  
//适用:小学生
//注意:该题,行是从1开始,到n, 列是从1开始,到m
//提交:信心满满,首次提交,竟然70分,WA了测试点6,7,10,一看他人问题,正中要害,是不是起点忘了标记,一看,果然如此,再次提交AC。
#include <stdio.h>
#include <string.h>
int a[10][10],vis[10][10];
int n,m,t,count=0;
int fr,fc;
int next[][2]={{-1,0},{1,0},{0,-1},{0,1}};//上 下 左 右
void dfs(int step,int row,int col){
    int nr,nc,i,j;
    if(row==fr&&col==fc){
        count++;
        return;
    }
    for(i=0;i<4;i++){
        nr=row+next[i][0];
        nc=col+next[i][1];
        if(nr>=1&&nr<=n&&nc>=1&&nc<=m)
            if(vis[nr][nc]==0&&a[nr][nc]==0){
                vis[nr][nc]=1;
                dfs(step+1,nr,nc);
                vis[nr][nc]=0;
            }
    }
}
int main(){
    int i,j,sr,sc,u,v;
    memset(a,0,sizeof(a));
    memset(vis,0,sizeof(vis));
    scanf("%d%d%d",&n,&m,&t);
    scanf("%d%d%d%d",&sr,&sc,&fr,&fc);
    for(i=1;i<=t;i++){
        scanf("%d%d",&u,&v);
        a[u][v]=1;
    }
    vis[sr][sc]=1;//遗漏此行代码,错了测试点6,7,10
    dfs(1,sr,sc);
    printf("%d\n",count);
    return 0;
}

2017-4-1

广度优先搜索

1.p1162 填涂颜色
难度:普及-
考点:输入,输出 ,矩阵,广度优先遍历 ,函数的编写,队列
适用:小学生
注意:过大的数组要开到main函数的外面。
过程:该题难在找出圈,思考如下,将矩阵最外圈进行扫描,遇到0,开始广度优先遍历,先将非圈内的0置为-1,
之后重新扫描,找到第一个0,开始广度优先遍历,将0置为2。
精髓,head取数据,tail存数据

#include <stdio.h>
#include <string.h>
int visited[30+10][30+10];
struct node{
    int r;
    int c;
}queue[900+100];
int n;
int next[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//上,下,左,右
void print(int r,int c){
    switch(visited[r][c]){
        case -1:
            printf("0");
            break;
        case 1:
            printf("1");
            break;
        case 2:
            printf("2");
            break;
    }
}
void bfs(int r, int c,int v){//r行c列v设置的值
    int i;
    int nextr,nextc;
    int head,tail;
    head=0;
    tail=0;
    queue[tail].r=r;
    queue[tail].c=c;
    tail++;
    visited[r][c]=v;
    while(head<tail){//精髓,head取数据,tail存数据
        for(i=0;i<4;i++){//上下左右四个方向
            nextr=queue[head].r+next[i][0];
            nextc=queue[head].c+next[i][1];
            if(nextr>=0&&nextr<n&&nextc>=0&&nextc<n&&visited[nextr][nextc]==0){
                queue[tail].r=nextr;
                queue[tail].c=nextc;
                tail++;
                visited[nextr][nextc]=v;
            }
        }
        head++;
    }
}
int main(){
    int i,j;
    memset(visited,0,sizeof(visited));
    scanf("%d",&n);
    for(i=0;i<n;i++)
        for(j=0;j<n;j++){
            scanf("%d",&visited[i][j]);
         }
    for(j=0;j<n;j++)//第0行
        if(visited[0][j]==0)
            bfs(0,j,-1);
    for(j=0;j<n;j++)//最后一行
        if(visited[n-1][j]==0)
            bfs(n-1,j,-1);
    for(i=0;i<n;i++)//第0列
        if(visited[i][0]==0)
            bfs(i,0,-1);
    for(i=0;i<n;i++)//最后一列
        if(visited[i][n-1]==0)
            bfs(i,n-1,-1);

    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
            if(visited[i][j]==0)//找圈
                bfs(i,j,2);
    
    for(i=0;i<n;i++){
        print(i,0);//将此处写成print(0,0);调了一会时间
        for(j=1;j<n;j++){
            printf(" ");
            print(i,j);
        }
        printf("\n");
    }
    return 0;
}

2.p1032 字串变换

NOIP 2002 提高组 复赛 字串变换

1.读完题目,样例模拟成功,但感觉该题挺难的,无从下手。

2.搜索网络后,才明白,因为有多种变换规则,故一次变换有多种可能,故采用广度优先遍历方法。

3.数据输入量:至多6个规则 所有字符串长度的上限为 20

4.感觉该题比较简单,编码,提交60分,测试点4,5 均RE。

5.将队列q[1000]改成q[10000],测试点4通过,测试点5还是RE,再改大数组还是无功而返。单向搜索,80分到头。

6.采用双向搜索,参考他人代码,收获很大,但提交还是80分,无奈只好对拍,才发现字符串变换出了问题,必须整个字符串搜索完毕,按规则逐个变换才能算一步变换结束,之前的问题是,字符串只变化第一次找到的字符,没有第二次第三次查找,算法存在较大问题。

7.问题找到后,开始修改,工程量不小。

8.核心代码:p=strstr(b+d+strlen(left[i]),left[i]);//查找剩下的字符串,该题最难之处

9.修改,提交AC。

该题整整编了四天。终于AC了,2017-4-11

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>
char input[100],output[100];
int lena=0;
struct node{
    char in[30];//in[100]
    int s;//步数
}q1[1000],q2[1000];//q[1000]
char left[10][30],right[10][30];//变换规则
void bfs(char *input,char *output){
    int h1,t1,i,j,d,h2,t2;
    char b[30],*p;//b[100]
    
    h1=t1=0;
    strcpy(q1[t1].in,input);
    q1[t1].s=0;
    t1++;
    
    h2=t2=0;
    strcpy(q2[t2].in,output);
    q2[t2].s=0;
    t2++;
    
    if(strcmp(q1[t1-1].in,q2[t2-1].in)==0){
        printf("%d\n",q1[t1-1].s+q2[t2-1].s);
        return;
    }
    while(h1<t1&&h2<t2){
        if(q1[h1].s+q2[h2].s>10)
            break;
        strcpy(b,q1[h1].in);
        for(i=0;i<lena;i++){//变换处理
            p=strstr(b,left[i]);
            while(p!=NULL){
                d=p-b;
                strncpy(q1[t1].in,b,d);
                strcat(q1[t1].in,right[i]);
                strcat(q1[t1].in,&b[d+strlen(left[i])]);
                q1[t1].s=q1[h1].s+1;
                for(j=0;j<t2;j++)
                    if(strcmp(q1[t1].in,q2[j].in)==0){
                        printf("%d\n",q1[t1].s+q2[j].s);
                        return;
                    }
                t1++;
                p=strstr(b+d+strlen(left[i]),left[i]);//查找剩下的字符串,该题最难之处
            }
        }
        h1++;
        
        strcpy(b,q2[h2].in);
        for(i=0;i<lena;i++){//变换处理
            p=strstr(b,right[i]);
            while(p!=NULL){//原来是if,现改成while
                d=p-b;
                strncpy(q2[t2].in,b,d);
                strcat(q2[t2].in,left[i]);
                strcat(q2[t2].in,&b[d+strlen(right[i])]);
                q2[t2].s=q2[h2].s+1;
                for(j=0;j<t1;j++)
                    if(strcmp(q2[t2].in,q1[j].in)==0){
                        printf("%d\n",q1[j].s+q2[t2].s);
                        return;
                    }
                t2++;
                p=strstr(b+d+strlen(right[i]),right[i]);//用了比较长的时间才理解。
            }
        }
        h2++;
    }
    printf("NO ANSWER!\n");
}
int main(){
    int i,j;
    char a[30],b[30];
    scanf("%s%s",input,output);//读取变换前后字符串
    while(scanf("%s%s",a,b)==2){//读取变换规则
        strcpy(left[lena],a);
        strcpy(right[lena],b);
        lena++;
    }
    bfs(input,output);
    return 0;
}

3.//p1126 机器人搬重物
//难度:普及/提高-
//考点:输入,输出 ,整数四则运算,取整,取模  
//适用:小学生
//注意:过大的数组要开到main函数的外面。
//小技巧:要设置一个计数变量
//陷阱:看了图才知道,机器人占有的空间是4格,编写之前一直误以为是1格。机器人直径1.6,网格1
//样例:模拟出12步的样例还是挺困难的:
//7 2 W 1
//7 1 W 2
//7 1 N 3
//4 1 N 4
//1 1 N 5
//1 1 E 6
//1 4 E 7
//1 5 E 8
//1 5 S 9
//2 5 S 10
//2 5 E 11
//2 7 E 12
//提交:70分WA了测试点7、测试点8、测试点10
//测试:经测试,无法到达的测试点是测试点4、测试点10
//仔细阅读他人代码,才发现,读题理解有问题,最后一行是机器人左上角位置坐标,目的地左上角坐标。
//还有一点,代码量小的不是一点点。代码量小,错误率低,重新按照最新理解,进行程序编写。
//操作整数比操作字符容易
//提交:重写的代码,错了测试点8、9 经查 4此处开了q[2500+100];太少,测试点8、9 WA
//收获:广度优先遍历水平更上一层楼,水平上升,妥妥的。2017-4-8 22:18
#include <stdio.h>
#include <string.h>
int next[][2]={{0,1},{1,0},{0,-1},{-1,0}};//东南西北 E0 S1 W2 N3 逆时针
struct node{
    int r;//行
    int c;//列
    int d;//方向 数字操作比字母方便
    int s;//步数
}q[10000+1000];//4此处开了q[2500+100];太少,测试点8、9 WA
int n,m;
int a[50+10][50+10];
int vis[50+10][50+10][5];
void bfs(int sr,int sc,int er,int ec,char dir){
    int head,tail,i,j;
    int nr,nc,nd;
    int d;
    int cr,cc,cd,cs;
    head=tail=0;
    q[tail].r=sr;
    q[tail].c=sc;
    switch(dir){
        case 'E':
            d=0;
            break;
        case 'S':
            d=1;
            break;
        case 'W':
            d=2;
            break;
        case 'N':
            d=3;
            break;
    }
    q[tail].d=d;
    q[tail].s=0;
    tail++;//2漏了该句,查了会
    vis[sr][sc][d]=1;
    while(head<tail){
        cr=q[head].r;
        cc=q[head].c;
        cs=q[head].s;
        cd=q[head].d;
        
        if(cr==er&&cc==ec){//包括起点与终点位置重合也考虑了
            printf("%d\n",cs);
            break;
        }
        for(i=1;i<=3;i++){//3步
            nr=cr+next[cd][0]*i;
            nc=cc+next[cd][1]*i;
            if(nr>=1&&nc>=1&&nr+1<=n&&nc+1<=m&&a[nr][nc]==0&&a[nr+1][nc]==0&&a[nr][nc+1]==0&&a[nr+1][nc+1]==0){//机器人占据四个位置,3笔误,写成a[nr][nc]==0&&a[nr+1][nr]==0&&a[nr][nc+1]==0&&a[nr+1][nc+1]==0
                if(vis[nr][nc][cd]==0){
                    vis[nr][nc][cd]=1;
                    q[tail].r=nr;
                    q[tail].c=nc;
                    q[tail].d=cd;
                    q[tail].s=cs+1;
                    tail++;
                }
            }else//第一步不能走,第二不就不能走;第二步不能走,第三步就不能走
                break;
        }
        //右转
        nd=(cd+1)%4;
        if(vis[cr][cc][nd]==0){
            vis[cr][cc][nd]=1;
            q[tail].r=cr;
            q[tail].c=cc;
            q[tail].d=nd;
            q[tail].s=cs+1;
            tail++;//1漏了该句,查了会
        }
        //左转
        nd=(cd-1+4)%4;
        if(vis[cr][cc][nd]==0){
            vis[cr][cc][nd]=1;
            q[tail].r=cr;
            q[tail].c=cc;
            q[tail].d=nd;
            q[tail].s=cs+1;
            tail++;//1漏了该句,查了会
        }
        head++;
    }
    if(head==tail)
        printf("-1\n");
}
int main(){
    int i,j,k,v;
    int sr,sc,er,ec;
    char d[5];
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++){
            scanf("%d",&v);
            a[i][j]=v;
            for(k=0;k<4;k++)
                vis[i][j][k]=v;//其中,将障碍物设置为访问过
        }
    scanf("%d%d%d%d%s",&sr,&sc,&er,&ec,d);
    bfs(sr,sc,er,ec,d[0]);
    return 0;
}


 

4.//p1141 01迷宫
//难度:普及/提高-
//考点:输入,输出 ,填色,广度优先遍历  
//适用:小学生
//注意:过大的数组要开到main函数的外面。
//陷阱:因m<=100000,故采用记忆化搜索,否则,必定超时。
//思考:在如何解决问题上,纠结了很长时间,一度要采用深度优先,但结束条件一直找不到,无奈,搜索文章,才发现是上色问题,采用广度优先。
//提交:测试点8,9,10TLE
//小技巧:遍历过程中,若遇到之前轮次访问过的点,那么两次遍历结果相同 ,3个点超时之后,查看他人写法,才弄明白。 修改提交,WA了测试点8,9,10.int ans[100000+100];//此处写成 ans[1000+10]查了半天  

#include <stdio.h>
#include <string.h>
char a[1000+10][1000+10];
int ans[100000+100];//此处写成 ans[1000+10]查了半天
int vis[1000+10][1000+10];
struct node{
    int r;
    int c;
}q[1000000+100];
int next[][2]={{-1,0},{1,0},{0,-1},{0,1}};//上 下 左 右
int n;
void bfs(int sr,int sc,int turn){
    int head,tail,nr,nc,cr,cc,i,j,sum=0;
    if(vis[sr][sc]){//之前轮次,已经访问过
        ans[turn]=ans[vis[sr][sc]];
        return;
    }
    head=tail=0;
    vis[sr][sc]=turn;
    q[tail].r=sr;
    q[tail].c=sc;
    tail++;
    while(head<tail){
        cr=q[head].r;
        cc=q[head].c;
        sum++;//取出队首,计数一次
        for(i=0;i<4;i++){
            nr=cr+next[i][0];
            nc=cc+next[i][1];
            if(nr>=0&&nr<n&&nc>=0&&nc<n&&(a[cr][cc]-'0')!=(a[nr][nc]-'0')){
                if(vis[nr][nc]&&vis[nr][nc]!=turn){//之前轮次访问过
                    ans[turn]=ans[vis[nr][nc]];
                    return;//不再搜索
                }else if(vis[nr][nc]==0){
                    vis[nr][nc]=turn;
                    q[tail].r=nr;
                    q[tail].c=nc;
                    tail++;
                }
                
            }
        }
        head++;
    }
    ans[turn]=sum;
}
int main(){
    int m,i,r,c;
    memset(ans,0,sizeof(ans));
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=0;i<n;i++)
        scanf("%s",a[i]);
    for(i=1;i<=m;i++){
        scanf("%d%d",&r,&c);
        bfs(r-1,c-1,i);
        printf("%d\n",ans[i]);
    }
    return 0;
}
 

5.//p1443 马的遍历
//难度:普及/提高-
//考点:输入,输出 ,广度(宽度)优先遍历,马走日知识  
//适用:小学生
//注意:过大的数组要开到main函数的外面。
//陷阱:宽5格,此句不容易注意到,跟踪了他人代码,才发现宽5格是指连数字,连符号一起占5个,为了找到这个问题,动用了对拍,该题是难在输出格式。
//思考:一直在想,这是什么棋,拿出样例模拟,才发现这是中国象棋,马走日。同一个马有八个方位可以走。
//收获:学习了输出左对齐,不足补空格的写法,队列的了解更深刻了。
//题目:经确认,题目的数据范围没有问题。
#include <stdio.h>
#include <string.h>
int n,m,sr,sc;
struct node{
    int r;
    int c;
    int s;
}qi[200000+1000];//400*400=160000
int step[400+10][400+10];
int vis[400+10][400+10];//漏了设置vis数组,查了好半天
int next[][2]={{-2,-1},{-2,1},{2,-1},{2,1},{-1,-2},{-1,2},{1,-2},{1,2}};//左上,右上,左下,右下,左上横,右上横,左下横,右下横
void bfs(int sr,int sc){//宽度优先遍历
    int head,tail;
    int nr,nc,i,j;
    head=tail=0;
    vis[sr][sc]=1;
    qi[tail].r=sr;
    qi[tail].c=sc;
    qi[tail].s=0;
    tail++;
    while(head<tail){//此处写成head<=tail,错了测试点10
        step[qi[head].r][qi[head].c]=qi[head].s;
        for(i=0;i<8;i++){
            nr=qi[head].r+next[i][0];
            nc=qi[head].c+next[i][1];
            if(nr>=1&&nr<=n&&nc>=1&&nc<=m&&vis[nr][nc]==0){//越界判定,此处写成 nr>=1&&nr<=n&&nc>=1&&nr<=m&&vis[nr][nc]==0 查了好半天,笔误啊
                vis[nr][nc]=1;
                qi[tail].r=nr;
                qi[tail].c=nc;
                qi[tail].s=qi[head].s+1;
                tail++;
            }
        }
        head++;
    }
}
int main(){
    int i,j;
    memset(vis,0,sizeof(vis));
    memset(step,-1,sizeof(step));
    scanf("%d%d%d%d",&n,&m,&sr,&sc);
    bfs(sr,sc);
    for(i=1;i<=n;i++){
        for(j=1;j<m;j++)
            printf("%-5d",step[i][j]);
        printf("%d\n",step[i][m]);
    }
    return 0;
}
 
2017-4-11

带有技巧的搜索

1.//p1118 [USACO06FEB]数字三角形 

//难度:普及/提高-
//考点:输入,输出 ,深度优先遍历,杨辉三角  
//适用:小学生
//提交:猜测,可能有超时。提交60分,测试点5 8 9 10TLE超时。
//看了他人做法后,才明白该题是杨辉三角,还是剪枝,只是效率比本人更好,不过程序得大改。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int a[20];
int f[20][20];
int vis[20];
int n,sum;
void dfs(int step,int b){
    int i,j;
    if(b>sum)//剪枝
        return;
    if(step==n+1){
        if(b==sum){
            printf("%d",a[1]);
            for(i=2;i<=n;i++)//1此句写成for(i=2;i<=n;i++)查了会
                printf(" %d",a[i]);//4此句写成printf(" %d",a[i][i]);查了会
            printf("\n");
            exit(0);//退出整个程序。
        }
        return;
    }
    for(i=1;i<=n;i++)
        if(vis[i]==0){
            vis[i]=1;
            a[step]=i;//2漏了此句,查了会
            dfs(step+1,b+a[step]*f[n][step]);//3此句写成 dfs(step+1,b+a[1][i]);查了会
            vis[i]=0;
        }
}
int main(){
    int i,j;
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&sum);
    for(i=1;i<=n;i++){
        f[i][1]=1;//第一列 1此处写成 a[1][i]=1查了好长时间
        f[i][i]=1;//对角线
    }
    for(i=3;i<=n;i++)
        for(j=2;j<=i-1;j++)
            f[i][j]=f[i-1][j-1]+f[i-1][j];//杨辉三角
    dfs(1,0);
    return 0;
}
 

2.//p1434 滑雪
//难度:普及/提高-
//考点:输入,输出 ,深度优先遍历,记忆化搜索。  
//适用:小学生
//注意:过大的数组要开到main函数的外面。
//提交:70分,早有预料,还是很满意的,至少深度优先搜索找最大步数,还是掌握得不错, 测试点3,5WA 测试点10 TLE
//int max=1;//3之前 int max=0;仔细想想第一个点就算一步。 修改此处过了测试点3,5
//测试点10 TLE,估计代码需要大改。
//采用剪枝,记忆化搜索,测试点10还是TLE
//if(step<d[r1][c1])//剪枝
//        return;
//    d[r1][c1]=step;//记忆
//为了通过测试点10,程序经历大改。
//去除了vis数组,与以往写法大有不同。因其数据,是按自大到小读取,并且采用记忆方式读取,故略去vis数组。
#include <stdio.h>
#include <string.h>
int a[100+10][100+10];
int d[100+10][100+10];
int r,c;
int next[][2]={{-1,0},{1,0},{0,-1},{0,1}};//上 下 左 右
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int dfs(int step,int r1,int c1){
    int i,j,nr,nc,t=1;//请注意,初始化t=1
    if(d[r1][c1]>0)
        return d[r1][c1];
    for(i=0;i<4;i++){
        nr=r1+next[i][0];
        nc=c1+next[i][1];
        if(nr>=1&&nr<=r&&nc>=1&&nc<=c&&a[r1][c1]>a[nr][nc]){//2此处写成a[r1][c1]>a[nr][nc]查了会
            t=fun(t,dfs(step+1,nr,nc)+1);
        }
    }
    d[r1][c1]=t;
    return t;
}
int main(){
    int i,j,max=1;
    memset(d,0,sizeof(d));
    scanf("%d%d",&r,&c);
    for(i=1;i<=r;i++)//1漏写了读取数据的循环
        for(j=1;j<=c;j++)
            scanf("%d",&a[i][j]);
    for(i=1;i<=r;i++)
        for(j=1;j<=c;j++){
            max=fun(max,dfs(1,i,j));//3此处写成 max=(max,dfs(1,i,j));查了会
        }
    printf("%d\n",max);
    return 0;
}
 

3.//p1433 吃奶酪
//带有技巧的搜索,只有528人过关,很明显,难度比较大,挑了一道最简单的普及-先来练手
//题目一看,消化样例用了会,模拟出来了,sqrt(2)+2+2+2=7.41
//模拟了一遍,决定采用深度优先遍历,
//提交50分,测试点1 5 6 9 TLE,测试点3 WA
//看了他人文章,突然间发现,输入可以是浮点数。 提交60分 测试点1 5 6 9 TLE
//剪枝,程序需要大改。
#include <stdio.h>
#include <string.h>
#include <math.h>
int n;
struct node{
    double x;
    double y;
}p[20];
int vis[20];
double min=999999;
void dfs(int step,double x,double y,double sum){
    int i;
    if(sum>min)//剪枝
        return;
    if(step==n+1){
        if(min>sum)
            min=sum;
        return;
    }
    for(i=1;i<=n;i++)
        if(vis[i]==0){
            vis[i]=1;
            dfs(step+1,p[i].x,p[i].y,sum+sqrt((p[i].x-x)*(p[i].x-x)+(p[i].y-y)*(p[i].y-y)));
            vis[i]=0;
        }
}
int main(){
    int i,j;
    memset(vis,0,sizeof(vis));
    scanf("%d",&n);
    p[0].x=0;
    p[0].y=0;
    for(i=1;i<=n;i++)
        scanf("%lf%lf",&p[i].x,&p[i].y);
    dfs(1,0,0,0);
    printf("%.2lf\n",min);
    return 0;
}

4.

NOIP 2009 提高组 复赛 sudoku 靶形数独

1.该年提高组最后一道(第四道),估计难度极大,代码量100+。

2.//noip2009 靶形数独 P1074 靶形数独
//http://www.cnblogs.com/zbtrs/p/5715422.html此文代码写得够短,值得研究 不过很遗憾,样例都通不过
//http://blog.csdn.net/yuyanggo/article/details/48711637此文代码写得漂亮,回溯,很像八皇后。开始研究,提交,在落谷中 ,测试点9,13 TLE 
//http://blog.sina.com.cn/s/blog_5d0d0f450100jm6u.html此文代码注释详细,标记一下,提交AC,而且时间效率很高 
//洛谷中遇到math.h必须以C++方式提交,否则,会报编译错误 ,这次以C语言方式提交,发现没有这个问题了。
//第一次提交,测试语句忘记删除,全WA,修改,提交,测试点1 WA ,在主函数里结尾阶段,加上if else  语句,提交AC 2017-9-24 20:42 
#include <stdio.h>
#include <string.h>
#include <math.h>
int a[11][11],r[11],rn[11],cn[11],xj[5][5],blank[11],rank[11],ans=0;//a[][]九宫格数据,r[]标记该行被占用的位,rn[]标记该行使用的数字 cn[]标记该列使用的数字 xj[][]标记该小九宫格使用的数字 blank[]需要填数的位数 
int calculate(){//计算分值
    int i,j,sum=0;
    for(i=1;i<=4;i++){//行处理 
        for(j=i;j<=10-i;j++){
            sum+=a[i][j]*(5+i);
            sum+=a[10-i][j]*(5+i);
        }
    }
    for(j=1;j<=4;j++)//列处理 
        for(i=j+1;i<=9-j;i++){//4 此处写成 for(i=j+1;i<=9-i;i++)
            sum+=a[i][j]*(5+j);
            sum+=a[i][10-j]*(5+j);
        } 
    sum+=a[5][5]*10;//中心处理 
    if(sum>ans)
        ans=sum;
}
void dfs(int k){
    int i,j,pos,x,y,p,z;
    if(k==10){
        calculate();return ;
    }
    i=rank[k];
    x=511-r[i];//r[i]中没有被占用的位,置1 
    y=x&-x;//从r[i]中没有占用的最低位取起
    j=(int)log2(y)+1;//转换为当前位置序号 
    pos=511-(rn[i]|cn[j]|xj[(i-1)/3][(j-1)/3]);//3 此处写成pos=511-(rn[i]|cn[i]|xj[(i-1)/3][(j-1)/3]); 没有被占用的数字,第一位表示1,第二位表示2,... 
    r[i]|=y;
    while(pos){
        p=pos&-pos;//取出没被占用的数字的最低位 
        pos-=p;//剩下没被占用的数字位置 
        z=(int)log2(p)+1;//转换为当前具体使用数字
        rn[i]|=p,cn[j]|=p,xj[(i-1)/3][(j-1)/3]|=p,a[i][j]=z;
        if(x==y) dfs(k+1);//只有1位没被占用,下一行 
        else dfs(k);//当前行 
        rn[i]-=p,cn[j]-=p,xj[(i-1)/3][(j-1)/3]-=p;//回溯 
    }
    r[i]-=y;

int main(){
    int i,j,p;
    memset(a,0,sizeof(a)),memset(r,0,sizeof(r)),memset(rn,0,sizeof(rn)),memset(cn,0,sizeof(cn)),memset(xj,0,sizeof(xj)),memset(blank,0,sizeof(blank));
    for(i=1;i<=9;i++)
        for(j=1;j<=9;j++){
            scanf("%d",&a[i][j]);
            if(a[i][j]){//非0
                r[i]|=1<<(j-1);//j-1位被占用 
                p=1<<(a[i][j]-1);//第一位代表数字1,第二位代表数字2,依次类推 
                if((rn[i]&p)!=0||(cn[j]&p)!=0||(xj[(i-1)/3][(j-1)/3]&p)!=0){//2 此处写成 if(rn[i]&p!=0||cn[j]&p!=0||xj[(i-1)/3][(j-1)/3]&p!=0) 1 此处写成 if(rn[i]&p!=0&&cn[j]&p!=0&&xj[(i-1)/3][(j-1)/3]&p!=0)应是或 
                    printf("-1\n");return 0;
                }
                rn[i]|=p,cn[j]|=p,xj[(i-1)/3][(j-1)/3]|=p;
            }else//0 
                blank[i]++;
        }
    for(i=1;i<=9;i++)
        rank[i]=i;
    for(i=1;i<=9;i++)
        for(j=i+1;j<=9;j++)
            if(blank[rank[i]]>blank[rank[j]]){//自小到大排列
                rank[i]^=rank[j];//位运算方式的交换 
                rank[j]^=rank[i];
                rank[i]^=rank[j];
            } 
    for(i=1;blank[rank[i]]==0;i++);//跳过不需要添数字的行 
    dfs(i);//从最少需要填数字的行开始深搜
    if(ans)//5添加该if else语句 对应测试点1 
        printf("%d\n",ans);
    else
        printf("-1\n"); 
    return 0;
}

2017-9-24 20:42 AC该单元

分治算法

1.//p1226 取余运算||快速幂
//很久以前接触过 快速幂 ,云里雾里
//这次翻看他人对快速幂的讲解,同时手动进行模拟,真的弄懂了。
//不容易,认识是螺旋式上升,第一次不懂,第二次似懂非懂,第三次弄懂,难能可贵
//提交,测试点4,5,6 WA
//下载 测试点4数据,之后将程序中的int 全改成long long 测试点4通过,提交AC

#include <stdio.h>
int main(){
    long long b,p,k,ans=1,a,n;
    scanf("%lld%lld%lld",&b,&p,&k);
    n=p;
    a=b;
    while(n){
        if(n&1)
            ans=(ans*a)%k;
        a=(a*a)%k;
        n>>=1;
    }
    printf("%lld^%lld mod %lld=%lld\n",b,p,k,ans);
    return 0;
}
 

2.//P1010 幂次方
//快速幂的学习还是有帮助的,至少能分离出幂次方
//因n<=20000故n<2^15故数组不会太大
//采用递归,程序核心数据计算没有问题,但打印又遇到困难,感觉有难度。
//硬着头皮,还是编好,样例测试通过,提交AC。
//收获,首次主动采用递归函数进行编程。难得。
#include <stdio.h>
#include <string.h>

void dy(int n){
    int a[20],i,k=0;
    memset(a,0,sizeof(a));
    while(n){
        if(n&1)
            a[k]=1;
        n>>=1;
        k++;
    }
    if(k-1==0)
        printf("2(0)");
    else if(k-1==1)
        printf("2");
    else if(k-1==2)
        printf("2(2)");
    else{
        printf("2(");
        dy(k-1);
        printf(")");
    }
        
    for(i=k-2;i>=0;i--)
        if(a[i]==1){
            if(i==0)
                printf("+2(0)");
            else if(i==1)
                printf("+2");
            else if(i==2)
                printf("+2(2)");
            else{
                printf("+2(");
                dy(i);
                printf(")");
            }
        }
}
int main(){
    int n;
    scanf("%d",&n);
    dy(n);
    return 0;
}

//P1908 逆序对 2018-10-25
//在线测评地址https://www.luogu.org/problemnew/show/P1908
//cnt+=mid-i+1;该句理解,此文介绍得不错https://www.cnblogs.com/cytus/p/7811856.html
//摘抄如下:
//如果a[i]>a[j]就会产生mid?i+1个逆序对,因为做归排的时候l~mid和mid+1~r都是已经排好序的所以如果a[i]>a[j]那么a[i+1]~a[mid]也就都大于a[j]
#include <stdio.h>
#define LL long long
#define maxn 500100
LL n,a[maxn],cnt=0,b[maxn];
void memorysort(LL left,LL right,LL mid){
    LL i,j,k=0;
    i=left,j=mid+1;
    while(i<=mid&&j<=right){
        if(a[i]>a[j]){
            cnt+=mid-i+1;
            b[k++]=a[j++];//此处写成 b[k++]=a[j]
        }
        else
            b[k++]=a[i++];
    }
    while(i<=mid)
        b[k++]=a[i++];
    while(j<=right)
        b[k++]=a[j++];
    for(i=0;i<k;i++)
        a[left+i]=b[i];
}
void mergesort(LL left,LL right){
    LL mid=(left+right)/2;
    if(left>=right)return;
    mergesort(left,mid);
    mergesort(mid+1,right);
    memorysort(left,right,mid);
}
int main(){
    LL i;
    scanf("%lld",&n);
    for(i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    mergesort(1,n);
    printf("%lld",cnt);
    return 0;
}

3.//p1908 逆序对
//样例很快模拟成功,该题比较简单,但是采用一般做法,算法复杂度O(n^2)要超时。
//查了查,有两种方法,一是树形数组,二是归并排序。趁此机会两种方法都掌握
//树形数组,先画图。
//看了http://blog.csdn.net/ljd4305/article/details/10101535搞懂了c[i]的计算
//假设A[]数组为存储原来的值得数组,C[]为树状数组。我们定义:C[i] = A[i - 2^k + 1] + ..... + A[i]  其中k为i用二进制表示时的末尾0的个数。例如:i= 10100,则k = 2,i = 11000,则k = 3;为了方便我直接写的是二进制的i,请读者注意。
//通过http://blog.csdn.net/int64ago/article/details/7429868弄懂了k的计算
//说这个之前我先说个叫lowbit的东西,lowbit(k)就是把k的二进制的高位1全部清空,只留下最低位的1,比如10的二进制是1010,则lowbit(k)=lowbit(1010)=0010(2进制),介于这个lowbit在下面会经常用到,这里给一个非常方便的实现方式,比较普遍的方法lowbit(k)=k&-k,这是位运算,我们知道一个数加一个负号是把这个数的二进制取反+1,如-10的二进制就是-1010=0101+1=0110,然后用1010&0110,答案就是0010了!明白了求解lowbit的方法就可以了,继续下面。介于下面讨论十进制已经没有意义(这个世界本来就是二进制的,人非要主观的构建一个十进制),下面所有的数没有特别说明都当作二进制。
//http://www.mobile-open.com/2016/937747.html也介绍得不错
//http://www.xuebuyuan.com/2234355.html有详细的题解,以下代码依本文推出。
//一直对 http://www.xuebuyuan.com/2234355.html逆序对的计算很是困惑,搜索了好多文章,直到找到该篇http://www.lai18.com/content/2054134.html弄懂了
//如果下标从0开始计算,那么对于数列中的第i个数,它前面一共有i个数字。也就是这个数字能产生逆序对的最大个数是i,此时如果能找到它前面的数字中,有k个比它小,也就是前面有k个数字不产生逆序对。那么第i个数字对逆序对的贡献就是i-k。此时我们就将问题转化成,求前i个数中有多少个数比第i个数小。可以在输入过程中对每个数的逆序数求解,建一个vis数组(初始化为0),只要输入一个数,在它的位置标记为1,然后计算出它的左边一共有多少数被标记了就可以知道多少个数比他小了,当然逆序数也就知道了,求从左到右数的和,这是树状数组最擅长的了。
//所谓i处逆序对,即是指以i元素为结尾对应的逆序对,这点,在认识上折腾了整整两天,终于弄明白了。可喜可贺。
//代码独立编程成功,样例通过后,提交AC 2017-5-3 21:31 开始时间2017-4-30 17:22
#include <stdio.h>
#include <string.h>
struct node{
    int v;
    int i;
}a[40000+100],a_t;
int b[40000+100],c[40000+100];
int n;
void quicksort(int left,int right){//自小到大
    int i=left,j=right;
    int mid=a[(left+right)/2].v;
    while(i<=j){
        while(a[i].v<mid)
            i++;
        while(a[j].v>mid)
            j--;
        if(i<=j){
            a_t=a[i];
            a[i]=a[j];
            a[j]=a_t;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort(left,j);
    if(i<right)
        quicksort(i,right);
}
//树状数组
int lowbit(int x){
    return x&-x;
}
void add(int x,int d){
    while(x<=n){
        c[x]+=d;
        x+=lowbit(x);    
    }
}
int sum(int x){
    int ans=0;
    while(x>0){
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}
int main(){
    int i,j,cnt=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i].v);
        a[i].i=i;
    }
    quicksort(1,n);//快速排序
    for(i=1;i<=n;i++){//离散化
        b[a[i].i]=i;
    }
    memset(c,0,sizeof(c));
    for(i=1;i<=n;i++){
        add(b[i],1);
        cnt+=i-sum(b[i]);//1此处写成cnt=i-sum(b[i]);,整个程序查了个遍
    }
    printf("%d\n",cnt);
    return 0;
}
 

4.

简单数学问题

1.

2.

3.//p1403 [AHOI2005]约数研究
//题目很快看懂,样例也很快模拟成功。
//但是一看数据范围,100%N<=1000000数了数6个0,明白该题一不小心,要超时。
//按题意进行编程,没有优化,按部就班,10000估计能通过测试,但100000等的时间远不止1s
//1000000,严重超时,提交,猜测得分30,结果得分20,测试点3-10,8个测试点TLE.
//估计程序要大改。参看他人题解,
//开始考虑从1~n去枚举每个数的约数个数,但时间上不允许,然后想到了从1~n去枚举每个约数有多少个。
//比如n=10. 包含约数1的数有10个,包含约数2的个数有10/2个,包含约数3的个数有10/3个……
//于是规律就有了。
//经测试,能到100000000 8个0 1s 真的很神奇,但是想不到。
#include <stdio.h>
int main(){
    int n,i,sum=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        sum+=n/i;
    printf("%d\n",sum);
    return 0;
}
4.//P1017 进制转换

NOIP2000 提高组 复赛 进制转换

1.该题难在弄懂样例,负进制,余数为>=0

2.试了一下,程序自带的/,%发现对负进制转换无用,得自个写一套。

3.弄懂了-15转-2进制,余数>=0,商正负都可以。模拟如下:

-15除-2=8余1

8除-2=-4余0

-4除-2=2余0

2除-2=-1余0

-1除-2=1余1

-15=110001

4.准备写chu,yu两个函数,对照上述模拟,写出了。

5.编码,样例很快通过,提交AC.

耗时:弄懂题意,模拟成功15分钟,编码20分钟

总耗时:35分钟

难度:中等。

附上AC代码,编译环境Dev-C++4.9.9.2

//2000 进制转换
#include <stdio.h>

int chu(int a,int b){//除的结果,a是整数,b是负进制
    int ans;
    int a1,b1;
    b1=b*(-1);
    if(a>0){//大于0
        if(a%b1==0)
            ans=a/b1*(-1);
        else{
            ans=a/b1*(-1);
        }
    }else{//小于0
        a1=a*(-1);
        if(a1%b1==0)
            ans=a1/b1;
        else{
            ans=a1/b1+1;
        }
    }
    return ans;
}
int yu(int a,int b){
    int ans;
    int a1,b1;
    b1=b*(-1);
    if(a>0){//大于0
        ans=a-chu(a,b)*b;
    }else{//小于0
        a1=a*(-1);
        ans=chu(a,b)*b1-a1;        
    }
    return ans;
}
int main(){
    int a,b;
    int a1,b1;
    int stack[100];
    int top;
    int v;
    scanf("%d%d",&a,&b);
    b1=b*(-1);
    top=-1;
    printf("%d=",a);
    while(!(a>0&&a<b1)){
        top++;
        stack[top]=yu(a,b);
        a=chu(a,b);
    }
    top++;
    stack[top]=a;
    while(top>=0){
        v=stack[top--];
        if(v<10)
            printf("%d",v);
        else{
            printf("%c",'A'+v-10);
        }
    }
    printf("(base%d)\n",b);
    return 0;
}

5.//p1147 连续自然数和
//读完题目,发现是等差数列,求和公式:
//(a+b)*(b+1-a)/2=m;进行计算
//b^2+b=2*m+a^2-a
//信心满满,采用C方式,提交,因sqrt一直编译不通过,采用C++方式提交,测试点1 2 4AC
//测试点3 5 6 7WA
//修改,提交,还是老问题,下载测试点3
//修改, for(i=1;i<=m/2;i++){//1此处写成 for(i=1;i<m/2;i++)错了测试点3,5
//测试点6 7WA,下载测试点6,突然发现运算过程中int 会越界,改成long long 提交AC
#include <stdio.h>
#include <math.h>
int main(){
    long long m,i,j,c;
    long long a,b;
    scanf("%lld",&m);
    for(i=1;i<=m/2;i++){//1此处写成 for(i=1;i<m/2;i++)错了测试点3,5
        a=2*m+i*i-i;
        a=4*a+1;
        b=(long long)sqrt(a);
        if(b*b==a)
            if((b-1)%2==0){
                b=(b-1)/2;
                if(b<i)
                    break;
                else
                    printf("%lld %lld\n",i,b);
            }
    }
    return 0;
}
 

6.

递推与递归二分

1.//P1192 台阶问题
//题目看完,模拟样例,发现竟然对不上。
//重来,
//5 2
//1 1 1
//2 11 2 2
//3 111 12 111 21 3
//4 1111 112 1111 121 1111 112 1111 211 4
//5
//样例模拟成功。
//找找规律,百思不得其解,参看此文http://blog.csdn.net/mrcrack/article/details/53418356才明白,想复杂了
//程序编好,样例通过,提交,只过了测试点1,测试点2-5全报TLE。
//参看他人写法,思路没问题,但用递归编写,是没没辙了,只能采取用空间换时间的策略,采用数组,循环形式处理。
//修改,提交,还是只过了测试点1,下载测试点2数据,一测试,发现漏模100003
//修改,提交,发现过了测试带点1,2 但测试点3,4,5圈RE
//读题发现:N ≤ 100000少数了0,马上修改。
#include <stdio.h>
#include <string.h>
int a[100000+100];//2 a[10000+100];
int main(){
    int n,k,i,j;
    scanf("%d%d",&n,&k);
    memset(a,0,sizeof(a));
    a[1]=1;
    for(i=2;i<=n;i++)
        for(j=1;j<=k;j++)
            if(i<=k){//i<=k
                if(i>j){
                    a[i]+=a[i-j];
                    a[i]%=100003;//1漏了该句
                }
                else{
                    a[i]+=1;
                    a[i]%=100003;//1漏了该句
                    break;
                }
            }else{//i>k
                a[i]+=a[i-j];
                a[i]%=100003;//1漏了该句
            }
    printf("%d\n",a[n]);
    return 0;
}

2.

3.//p1057 传球游戏
//该题与 P1164 小A点菜 有点 象
//模拟样例,一直觉得挺奇怪的,应该有很多种啊,一模拟,发现1->2->1是不行的。
//样例弄懂的,很多时候弄懂样例,纸笔比在脑子里凭空想想,更有效。
//没感觉,翻看https://wenku.baidu.com/view/5b9dfd5abe23482fb4da4c40.html
//直接dp,似乎说递推更确切点。
//f(i,k)表示经过k次传到编号为i的人手中的方案数。那么可以推出下面的方程:
//f(i,k)=f(i-1,k-1)+f(i+1,k-1)(i=1或n时,需单独处理)
//边界条件:f(1,0)=1;结果在f(1,m)中
//大致有些感觉,两个量,一是i人,二是k次
//编的过程中,发现下手有些困难,但之前有动态规划的经验,还是硬写下来,但样例没法通过
//手动模拟一编,发现问题,马上修改,样例通过,提交RE,翻看他人代码,才发现
//a=i-1;//1此处漏
//b=i+1;//1此处漏
//很小的两处问题。   
#include <stdio.h>
#include <string.h>
int f[30+10][30+10];
int main(){
    int n,m,i,j,a,b;
    scanf("%d%d",&n,&m);
    memset(f,0,sizeof(f));
    f[1][0]=1;
    for(j=1;j<=m;j++)
        for(i=1;i<=n;i++){
            a=i-1;//1此处漏
            b=i+1;//1此处漏
            if(i-1==0){
                a=n;
            }
            else if(i+1>n)
                b=1;
            f[i][j]=f[a][j-1]+f[b][j-1];
        }
    printf("%d\n",f[1][m]);
    return 0;
}
 

4.

5.//P1216 [USACO1.5]数字三角形 Number Triangles
//第一想法,深度优先遍历算法复杂度2^n,显然不行。
//《算法竞赛入门经典(第2版)》第9章,对该问题,有详尽的讨论,当时以为看懂了,实际编程不行
//还是要把书翻出来研究。
//为解决问题,而作的学习,效率极高,发现问题讲解最好有配套例题,事半功倍。书应该这么写。
//书看好了,好家伙,一气讲了3种方法,采用动态规划常用的循环方法。采用方法2,这回不仅看懂,编码也一次AC
#include <stdio.h>
int a[1000+10][1000+10],d[1000+10][1000+10];
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int n,i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        for(j=1;j<=i;j++)
            scanf("%d",&a[i][j]);
    for(j=1;j<=n;j++)//初始化最下层数据
        d[n][j]=a[n][j];
    for(i=n-1;i>=1;i--)
        for(j=1;j<=i;j++)
            d[i][j]=a[i][j]+fun(d[i+1][j],d[i+1][j+1]);
    printf("%d\n",d[1][1]);
    return 0;
}
 
 

6.

7.

线性数据结构

1.//P1996 约瑟夫问题
//该题是一道简单模拟题,按部就班即可。
//难点在于判断是否全部出列,当然也是容易解决
#include <stdio.h>
#include <string.h>
int book[100+10];
int main(){
    int n,m,i,flag=0,count=0;
    memset(book,0,sizeof(book));
    scanf("%d%d",&n,&m);
    while(flag==0){
        for(i=1;i<=n;i++)
            if(book[i]==0){
                count++;
                if(count==m){
                    count=0;
                    book[i]=1;
                    printf("%d ",i);
                }
            }
        flag=1;
        for(i=1;i<=n;i++)//全部出列判断。
            flag*=book[i];
    }
    printf("\n");
}

 

2.//p1115 最大子段和
//一看题目,被问题吓了提交,找出最大子段和,挺复杂啊。
//看了看数据N<=200000,突然有了思路,计算到达每个位置的连续和。
//找出连续和中最小值,最大值,两个相减,即为答案。
//提交WA,没想法了,这么清晰的思路,结果竟然是这样。
//仔细想了想,还有一个问题,max的位置应该在min的后面,否则,就只取max
//修改,提交,只过了测试点1
//猜测可能数据较大,int 不够。改成long long 还是只过了测试点1
//要能过,必须得采用高精度算法,或者换算法
//动态规划,d[i]表示当前最大值,普通算法,该题基本要数据溢出,基于这种考虑,选择动态规划。
#include <stdio.h>
int a[200000+100];
int d[200000+100];
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int n,i,j,k;
    int max=-99999999;
    scanf("%d",&n);
    for(i=1;i<=n;i++){//数据读取
        scanf("%d",&a[i]);
        d[i]=a[i];
    }
    for(i=2;i<=n;i++)//计算到达i位置的最大和
        d[i]=fun(d[i],d[i-1]+a[i]);
    for(i=1;i<=n;i++)
        if(max<d[i])//找最大值
            max=d[i];
    printf("%d\n",max);
    return 0;
}

 

3.//P1739 表达式括号匹配
//读完题目,发现是字符串的处理。
//采用栈的方式,遇到左括号进栈,遇到右括号,左括号出栈
//有更简单的方式,统计左括号数量,右括号数量
#include <stdio.h>
#include <string.h>
char input[300];
int main(){
    int len,i,j,left=0,right=0;
    scanf("%s",input);
    len=strlen(input);
    for(i=0;i<len;i++)
        if(input[i]=='(')
            left++;
        else if(input[i]==')')
            right++;
    if(left==right)
        printf("YES\n");
    else
        printf("NO\n");
    return 0;
}

 

4.//P1160 队列安排
//初读题目,讲的是队列,但对程序进行模拟,发现该题用链表操作比较方便。
//编写完成后,发现画图理解链表,写起程序来,才会出错少。
//编写完成,提交,通过测试点1 2,但测试点3 4 5 TLE。
//翻看他人做法,发现指针形式的链表,超时是必然,要想AC,只能采用数组形式的链表,在搜索这块,降低时间复杂度。
//稍有些感觉,数组形式链表,不仅仅是链表的另一种写法,更重要的是能省时。
//采用双向链表,数组形式。编码,提交,全部WA。
//重读题目,发现关键一句:行末换行且无空格。马上着手进行修改。提交WA
//很想看看测试数据,但这是不可能的,耐着性子,将代码又读了一遍,发现一处问题,
//left[right[k]]=left[k];//2此处写成 left[right[k]]=left[left[k]];查了会 ,也即一直全部数据WA的原因. 修改,提交AC
//2017-4-21
#include <stdio.h>
#include <string.h>
int d[100000+100],left[100000+100],right[100000+100];
int main(){
    int n,m,i,j,head,k,p;
    memset(d,0,sizeof(d));
    scanf("%d",&n);
    head=1;
    d[1]=1;
    left[1]=-1;
    right[1]=-1;
    for(i=2;i<=n;i++){//插入操作
        scanf("%d%d",&k,&p);
        if(p==0){//左侧
            if(head==k){//头部
                d[i]=i;
                left[i]=left[head];
                right[i]=head;
                left[head]=i;
                head=i;
            }else{//非头部
                d[i]=i;
                right[left[k]]=i;
                left[i]=left[k];
                right[i]=k;
                left[k]=i;
            }
        }else{//右侧
            d[i]=i;
            left[i]=k;
            right[i]=right[k];
            left[right[k]]=i;//1漏了该句 ,查了好久
            right[k]=i;
        }
        
    
    }
    scanf("%d",&m);
    for(i=1;i<=m;i++){//删除操作
        scanf("%d",&k);
        if(k==head){//头部
            left[right[head]]=left[head];
            head=right[head];
        }else{//非头部
            if(d[k]!=0){
                d[k]=0;
                right[left[k]]=right[k];
                left[right[k]]=left[k];//2此处写成 left[right[k]]=left[left[k]];查了会 ,也即一直全部数据WA的原因.
            }
        }
    }
    printf("%d",d[head]);
    p=right[head];
    while(p!=-1){
        printf(" %d",d[p]);
        p=right[p];
    }
    printf("\n");
    return 0;
}

2017-4-21

5.//p1449 后缀表达式
//第一直觉就是用栈
//但看了样例,皱眉头了,输入的数据读取解析需花功夫。
//在解析字符串上花了大量的时间,代码量编写长度不小,样例通过后,提交比较忐忑,看到的结果是AC
//有《大话数据结构》的功劳,里面讲过后缀表达式的处理,采用栈的操作,一直没有机会编码,如今在该题实现了编码。
//还是那句话,有了思路以后,解析字符串耗费了大量的精力。没有充分的午休,该题是很难编写成功的。
//编好后还不放心,在脑袋中模拟了一遍程序的运行。
//看书的收获不是记了多少代码,而是掌握了处理问题的方法,具体代码,编写熟练后,能根据方法写出,换句话,人机交流比较熟练后,这些都不是问题。
#include <stdio.h>
#include <string.h>
char input[1000+10],sub[1000+10];
int stack[1000+10];
int ch2in(char *s){//字符转化成数字
    int len,i,ans=0;
    len=strlen(s);
    for(i=0;i<len;i++){
        ans*=10;
        ans+=s[i]-'0';
    }
    return ans;
}
int main(){
    int i,j=0,len,top=-1,len2,a,b;//top指向当前栈顶元素
    scanf("%s",input);
    len=strlen(input);
    for(i=0;i<len;i++){
        if(input[i]=='.'||input[i]=='@'){
            sub[j]='\0';
            j=0;
            len2=strlen(sub);
            if(sub[0]>='0'&&sub[0]<='9'){//数字
                 a=ch2in(sub);
                 top++;
                 stack[top]=a;
            }
        }else if(input[i]=='+'||input[i]=='-'||input[i]=='*'||input[i]=='/'){
            a=stack[top];
            top--;
            b=stack[top];
            top--;
            top++;
            switch(input[i]){
                case '+':
                    stack[top]=b+a;
                    break;
                case '-':
                    stack[top]=b-a;
                    break;
                case '*':
                    stack[top]=b*a;
                    break;
                case '/':
                    stack[top]=b/a;
                    break;
            }
        }else{
            sub[j++]=input[i];
        }
    }
    printf("%d\n",stack[top]);
    return 0;
}

AC该单元 2017-4-21

树形数据结构

1.P1087 FBI树

NOIP 2004 普及组 复赛 FBI树

1.阅读题目,还有些不知所云。

2.对样例进行手动模拟,弄明白题意了。

10001011

 FBI树如下图所示

F

F     F

F  B  F  I

I B B B I B I I

1) T的根结点为R,其类型与串S的类型相同;此句是核心中的核心,也即F B I三种根节点。

3.接下来编程实现是重点。先建树,子弟相信行一层一层建树,用一维数组存储,第1号(第0号不使用)元素开始使用数组。父k,左子2*k,右子2*k+1。再进行遍历,后序遍历,采用递归的方式。

4.建树函数,遍历函数编写。

5.代码编写完成,提交AC,递归函数写法,记得先写终结条件。

6.此文写得不错,读者也可以借鉴。http://blog.csdn.NET/dogeding/article/details/52727239

7.此题,可以借鉴的是:二叉树的存储,二叉树的遍历。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>

int n;
char a[1024+10];
char b[2048+10];

void build(char *s,int n){//建树代码
    //底层构造
    int i,j=0;
    for(i=(1<<n);i<=(1<<(n+1))-1;i++){
        b[i]=a[j++]=='0'?'B':'I';
    }
    
    //自底向上构造
    for(i=n-1;i>=0;i--)
        for(j=(1<<i);j<=(1<<(i+1))-1;j++)
            if(b[2*j]=='B'&&b[2*j+1]=='B')
                b[j]='B';
            else if(b[2*j]=='I'&&b[2*j+1]=='I')
                b[j]='I';
            else
                b[j]='F';
}
void visit(int node){//后序遍历代码
    if(node>(1<<(n+1))-1)
        return;
    visit(node*2);//左子树
    visit(node*2+1);//右子树
    printf("%c",b[node]);//根
}
int main(){
    scanf("%d",&n);
    scanf("%s",a);
    build(a,n);
    visit(1);
    printf("\n");
    return 0;
}

2.P1030 求先序排列

NOIP 2001 普及组 复赛 求先序排列

1.初赛,这种题目做得多了去,但要上机写代码,突然捱了,尽然觉得有些困难。

2.不过,经验告诉我们,能用笔模拟出,那么也能用计算机算出。

3.目前,就是要靠手动模拟,找出规律。

4.//p1030 求先序排列
//http://blog.csdn.NET/yuyanggo/article/details/47955837此文写得不错。
//根据上述文章进行编写,发现处理字符串位置时,十分困难,决定对程序进行改进,以方便编写为主
//经过一番调试,样例通过,提交AC。自认为本人代码量虽然大了些,但从编写角度,更容易写出,所谓“青出于蓝”
#include <stdio.h>
#include <string.h>
char in[10],post[10];//in中序 post后续
void pre(char *s1,char *s2){//s1中序  s2后序
    char *p,s3[10],s4[10];
    int k,len,i;
    len=strlen(s1);
    if(len==0)
        return;
    printf("%c",s2[len-1]);
    p=strchr(s1,s2[len-1]);
    k=p-s1;//此处忘记修改 k=p-in; 查了会
    for(i=0;i<k;i++)
        s3[i]=s1[i];
    s3[i]='\0';
    for(i=0;i<k;i++)
        s4[i]=s2[i];
    s4[i]='\0';
    pre(s3,s4);//左子树
    for(i=0;i<len-k-1;i++){
        s3[i]=s1[k+1+i];
    }
    s3[i]='\0';
    for(i=0;i<len-k-1;i++)
        s4[i]=s2[k+i];
    s4[i]='\0';
    pre(s3,s4);//右子树
}
int main(){
    int len;
    scanf("%s%s",in,post);
    pre(in,post);
    return 0;
}

3.//P1305 新二叉树 本题思路类似 P1087 FBI树
//该题输入感觉是二叉树按层进行输入。自上而下,自左往右
//对数据进行模拟,发现规律
//abcbdicj*d**i**j**
//abcdij
//样例通过后,提交AC,一看才两个测试点。2017-5-5
#include <stdio.h>
#include <string.h>
int lenb;
char a[10000],b[10000];
void pre(int node){
    if(node>lenb)
        return ;
    printf("%c",b[node-1]);
    pre(node*2);//左子树
    pre(node*2+1);//右子树
}
int main(){
    int n,i,j=0,len;
    char t[10];
    scanf("%d",&n);
    scanf("%s",a);
    for(i=2;i<=n;i++){
        scanf("%s",t);
        strcat(a,t);
    }
    len=strlen(a);
    for(i=0;i<len;i++)
        for(j=i+1;j<len;j++)
            if(a[i]==a[j])
                a[j]='*';
    j=0;//1 漏了此句,查得有点久
    for(i=0;i<len;i++)
        if(a[i]!='*'){
            b[j++]=a[i];
        }
    b[j]='\0';
    lenb=strlen(b);
    pre(1);
    return 0;
}
 

2017-5-5 AC该单元。

动态规划的背包问题

1.p1060 开心的金明

NOIP 2006 普及组 复赛 happy 开心的金明

1.读完题目,模拟样例,一直很纳闷3900是怎么算出的,同种物品能买多件吗?

2.同种物体只能买一件,从题目中怎么读出该题意?反复读题,题中透着此意,但不明确。

3.模拟样例:400*5+300*5+200*2=3900

4.识别出该题是01背包问题,但发现目前所掌握的动态规划,还不足以解决该问题。

5.搜索了一通,一下两篇文章值得学习:

http://blog.csdn.NET/mu399/article/details/7722810

https://wenku.baidu.com/view/b7b9c83f9b89680203d825fd.html

6.弄懂了01背包。还不急着处理本题,先将http://blog.csdn.Net/mu399/article/details/7722810例子编编。编着编着,突然发现,同样是处理01背包问题,程序编起来,循环的差异却非常大,看来要好好研究。

7.附上http://blog.csdn.net/mu399/article/details/7722810例子的输入输出:

输入:

10 5
4 6
5 4
6 5
2 3
2 6

输出:

15

代码:

#include <stdio.h>
#include <string.h>
int fun(int a,int b){//返回最大值
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int w[10],v[10],n,m,i,j,f[20][20];
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&w[i],&v[i]);
    for(i=0;i<=m;i++)
        f[i][0]=0;//空间为0
    for(i=0;i<=n;i++)
        f[0][i]=0;//选中个数为0
    for(j=0;j<=n;j++)
        for(i=1;i<=m;i++)
            if(j<w[i])
                f[i][j]=f[i-1][j];
            else
                f[i][j]=fun(f[i-1][j],f[i-1][j-w[i]]+v[i]);
    printf("%d\n",f[m][n]);
}
8.回到本题,看了N(<30000)表示总钱数,m(<25)范围,N*m不会超时。

9.按01背包思路,进行编写,样例通过,提交AC,就是耗时56ms多了些,要找书来学习《挑战程序设计竞赛》。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int f[30][30000+100];
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int v[30],p[30],i,j,n,m;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&v[i],&p[i]);
    for(i=0;i<=m;i++)
        f[i][0]=0;//0元钱
    for(i=0;i<=n;i++)
        f[0][i]=0;//挑0个
    for(j=0;j<=n;j++)
        for(i=1;i<=m;i++)
            if(j<v[i])
                f[i][j]=f[i-1][j];
            else
                f[i][j]=fun(f[i-1][j],f[i-1][j-v[i]]+v[i]*p[i]);
    printf("%d\n",f[m][n]);
    return 0;
}

2017-4-13 21:58

10.觉得上述代码写得挺别扭的,重新搜索,找到好文http://blog.csdn.net/wumuzi520/article/details/7014559里面有比较顺手的数据生成过程,与http://blog.csdn.net/mu399/article/details/7722810刚好形成对称关系,本人更欣赏现在这篇文章的写法。认识到了《背包九讲》,最新下载https://github.com/tianyicui/pack/blob/master/V2.pdf

11.要花时间学习。

附上觉得思路还比较顺的AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int v[10000+100],p[10000+100];
int f[30][30000+100];
int main(){
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&v[i],&p[i]);
    for(i=0;i<=n;i++)
        f[0][i]=0;//0种物品进行选择
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            if(j<v[i])
                f[i][j]=f[i-1][j];
            else
                f[i][j]=fun(f[i-1][j],f[i-1][j-v[i]]+v[i]*p[i]);
    printf("%d\n",f[m][n]);
    return 0;
}

趁热打铁,顺便把二维数组化成一维数组的代码,给学习了,一开始翻了很多书都不明白,但跟踪了程序,打印出关键数据进行研究,弄懂了,附上AC的一维数组代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
#include <string.h>
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int v[10000+100],p[10000+100];
int f[30000+100];
int main(){
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&v[i],&p[i]);
    memset(f,0,sizeof(f));//初始化数组
    for(i=1;i<=m;i++)
        for(j=n;j>=1;j--)
            if(j>=v[i])
                f[j]=fun(f[j],f[j-v[i]]+v[i]*p[i]);
    printf("%d\n",f[n]);
    return 0;
}

2017-4-17

2.//p1164 小A点菜
//该题是01背包问题,但与通常求最优解不同,是统计装满个数
//模拟了样例,发现即是统计达到n时,最优解等于n的个数。(事后发现错误)
//参考他人代码,但终归不是自个思路,难以理解,不过,还是有启发的。
//在启发下,按自个思路进行手动模拟,发现能解决问题,马上编码。提交WA了一片。
//怎么理解下面的代码,f[i][j]前i件物品中选取若干件物品放入剩余空间为j的背包的方案总数
//f[i][j]=f[i-1][j];很好理解,i件物品无法放入,那么总数f[i][j]=f[i-1][j];
//f[i][j]=f[i-1][j]+f[i-1][j-v[i]];怎么理解,f[i-1][j]表示i件物品没有放入时的方案总数。
//f[i-1][j-v[i]表示i件物品放入时的方案总数,怎么理解,如下所示:
//abc acd bcd放入i
//abci acdi bcdi没有改变方案总数.
//理解 f[i][j]=f[i-1][j]+f[i-1][j-v[i]];花了好长时间。总算弄明白了
#include <stdio.h>
#include <string.h>
int f[100+10][10000+100];
int v[100+10];
int main(){//n种菜,m元钱
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%d",&v[i]);
    memset(f,0,sizeof(f));//建议此处初始化,这样程序的数据就十分清晰
    f[0][0]=1;
    for(i=1;i<=n;i++)//1此处写成 for(i=1;i<=m;i++)
        for(j=0;j<=m;j++)//1此处写成 for(j=1;j<=n;j++)//2此处写成 for(j=1;j<=m;j++)
            if(j<v[i])
                f[i][j]=f[i-1][j];
            else
                f[i][j]=f[i-1][j]+f[i-1][j-v[i]];//f[i-1][j]未加入i的方案数 f[i-1][j-v[i]]加入i的方案数 两种方案数互斥,故可相加。
    printf("%d\n",f[n][m]);
    return 0;
}

//对空间进行优化
#include <stdio.h>
#include <string.h>
int f[10000+100];
int v[100+10];
int main(){//n种菜,m元钱
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%d",&v[i]);
    memset(f,0,sizeof(f));//建议此处初始化,这样程序的数据就十分清晰
    f[0]=1;
    for(i=1;i<=n;i++)//1此处写成 for(i=1;i<=m;i++)
        for(j=m;j>=0;j--)//1此处写成 for(j=1;j<=n;j++)//2此处写成 for(j=1;j<=m;j++)
            if(j>=v[i])
                f[j]=f[j]+f[j-v[i]];//f[i-1][j]未加入i的方案数 f[i-1][j-v[i]]加入i的方案数 两种方案数互斥,故可相加。
    printf("%d\n",f[m]);
    return 0;
}

2017-4-17

3.

NOIP 2006 提高组 复赛 budget 金明的预算方案

1.题目中透着01背包问题,样例也模拟出来了:400*3+500*2=2200

2.如何选择主次,存在疑虑。

//p1064 金明的预算方案
//读完题目,有条件限制的01背包问题
//编编看,看是否能理清关系
//第一步,排序,按主件在前,附件在后的办法进行处理。
//写完代码,测试样例都无法通过,明白该题得寻求外界帮助。
//《新编全国青少年信息需竞赛培训教材(复赛篇)》虽是pascal,但里面关于该题的叙述还是很有参考价值
//重写代码。 采用一维数组
//该题编写经历了三天,01背包问题,有了长足的进步。
#include <stdio.h>
#include <string.h>
int f[3200+100];//此处写成 f[60+10]
int v[60+10][3],p[60+10][3];//0主件1附件1 2附件2
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int N,m,i,j,a,b,c,n,v0,v1,v2,p0,p1,p2;
    memset(v,0,sizeof(v));
    scanf("%d%d",&N,&m);
    n=N/10;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        if(c==0)
            c=i;
        for(j=0;j<=2;j++)
            if(v[c][j]==0){//1此处写成 v[i][j]==0
                v[c][j]=a/10;//缩小10倍
                p[c][j]=v[c][j]*b;
                break;//4漏了break,调了好长时间。是跟踪了代码,才发现此处有问题。
            }
    }
    
    for(i=0;i<=n;i++)
        f[i]=0;
    for(i=1;i<=m;i++)
        for(j=n;j>=0;j--)
            if(v[i][0]>0){//2漏了此条件
                v0=v[i][0];
                v1=v[i][1];
                v2=v[i][2];
                p0=p[i][0];
                p1=p[i][1];
                p2=p[i][2];
                if(j>=v0)
                    f[j]=fun(f[j],f[j-v0]+p0);
                if(j>=v0+v1)
                    f[j]=fun(f[j],f[j-v0-v1]+p0+p1);
                if(j>=v0+v2)//3漏了此种情况
                    f[j]=fun(f[j],f[j-v0-v2]+p0+p2);
                if(j>=v0+v1+v2)
                    f[j]=fun(f[j],f[j-v0-v1-v2]+p0+p1+p2);
            }
    printf("%d\n",f[n]*10);
}

2017-4-19 22:20

4.p1048 采药

NOIP 2005 普及组 复赛 medic 采药

1.样例比较简单,很快就模拟成功,该题是01背包问题。

2.很快编码成功,样例通过,提交10分,只对了测试点1。

3.一查,竟然是数组开得太小了,int f[100+10][1000+10];//1此处写成 f[100+10][100+10];修改提交AC。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int t[100+10],v[100+10];
int f[100+10][1000+10];//1此处写成 f[100+10][100+10];
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int T,m,i,j;
    scanf("%d%d",&T,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&t[i],&v[i]);
    for(i=0;i<=T;i++)
        f[0][i]=0;
    for(i=1;i<=m;i++)
        for(j=0;j<=T;j++)
            if(j<t[i])
                f[i][j]=f[i-1][j];
            else
                f[i][j]=fun(f[i-1][j],f[i-1][j-t[i]]+v[i]);
    printf("%d\n",f[m][T]);
}
 

4.优化空间

#include <stdio.h>
int t[100+10],v[100+10];
int f[1000+10];//1此处写成 f[100+10][100+10];
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int T,m,i,j;
    scanf("%d%d",&T,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&t[i],&v[i]);
    for(i=0;i<=T;i++)
        f[i]=0;
    for(i=1;i<=m;i++)
        for(j=T;j>=0;j--)//1此处写成 for(j=0;j<=T;j++)
            if(j>=t[i])
                f[j]=fun(f[j],f[j-t[i]]+v[i]);
    printf("%d\n",f[T]);
}

5.//p1049 装箱问题

NOIP 2001 普及组 复赛 装箱问题

1.一眼看出01背包问题。

2.剩下最小体积,可以看成装载最大体积,之后总体积扣除,即得答案。

3.样例很快通过,提交AC。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int v[30+10];
int f[30+10][20000+10];
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int V,n,i,j;
    scanf("%d%d",&V,&n);
    for(i=1;i<=n;i++)
        scanf("%d",&v[i]);
    for(i=0;i<=V;i++)
        f[0][i]=0;
    for(i=1;i<=n;i++)
        for(j=0;j<=V;j++)
            if(j<v[i])
                f[i][j]=f[i-1][j];
            else
                f[i][j]=fun(f[i-1][j],f[i-1][j-v[i]]+v[i]);
    printf("%d\n",V-f[n][V]);
    return 0;
}

4.空间优化,采用一维数组。

#include <stdio.h>
int v[30+10];
int f[20000+10];
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int V,n,i,j;
    scanf("%d%d",&V,&n);
    for(i=1;i<=n;i++)
        scanf("%d",&v[i]);
    for(i=0;i<=V;i++)
        f[i]=0;
    for(i=1;i<=n;i++)
        for(j=V;j>=0;j--)
            if(j>=v[i])
                f[j]=fun(f[j],f[j-v[i]]+v[i]);
    printf("%d\n",V-f[V]);
    return 0;
}

6.//p1616 疯狂的采药
//读完题目,这是一个完全背包问题。
//因空间问题,只能采用一维数组。
//完全背包 f[j]=fun(f[j],f[j-t[i]]+v[i]);f[j]最新  f[j]新  f[j-t[i]]+v[i]新 同一轮次计算出的结果
//这篇文章写得不错,值得细细品读http://blog.csdn.net/wumuzi520/article/details/7014830
#include <stdio.h>
int f[100000+10],t[10000+10],v[10000+10];
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int T,m,i,j;
    scanf("%d%d",&T,&m);
    for(i=1;i<=m;i++)
        scanf("%d%d",&t[i],&v[i]);
    for(i=0;i<=T;i++)
        f[i]=0;
    for(i=1;i<=m;i++)
        for(j=t[i];j<=T;j++)
            f[j]=fun(f[j],f[j-t[i]]+v[i]);
    printf("%d\n",f[T]);
    return 0;
}
2017-4-19 22:20 AC该篇

线性动态规划

1.//p1020 导弹拦截

NOIP 1999 提高组 复赛  拦截导弹

1.该题一看完,马上确定是动态规划问题,对应经典模型:最大上升子序列。

2.该题是最大下降子序列。

3.最多能拦截几枚,处理好,但最少几套系统,却不清楚,几次想采用偏分,1,2,未果

4.搜索http://blog.csdn.NET/lyhvoyage/article/details/8537049介绍得不错

由于炮弹的发射高度是递减的,如果后面的导弹的高度大于前面的高度,就不能把后面的那颗导弹拦截,若想拦截,就要增加一个拦截系统。问题的实质就是求出最长的连续递增子序列的长度。

5.不过能将几枚做好,已无精力处理几套了。

6.修改程序,样例通过,提交AC。

附上AC代码,编译环境Dev-C++4.9.9.2

//1999 导弹拦截
#include <stdio.h>
#include <string.h>
int a[100+10];
int d[100+10];
int t[100+10];
int main(){
    int v;
    int n=0;
    int i,j;
    int maxd,maxt;
    memset(a,0,sizeof(a));
    memset(d,0,sizeof(d));
    memset(t,0,sizeof(t));
    while(scanf("%d",&v)==1){
        a[n]=v;
        n++;
    }
    
    //下降序列
    d[0]=1;
    for(i=1;i<n;i++){
        d[i]=1;
        for(j=0;j<i;j++){
            if(a[j]>a[i]&&d[j]+1>d[i])
                d[i]=d[j]+1;            
        }
    }
    
    //上升序列
    t[0]=1;
    for(i=1;i<n;i++){
        t[i]=1;
        for(j=0;j<i;j++){
            if(a[j]<a[i]&&t[j]+1>t[i])
                t[i]=t[j]+1;
            
        }
    }
    
    maxd=1;
    maxt=1;
    for(i=0;i<n;i++){
        if(maxd<d[i])
            maxd=d[i];
        if(maxt<t[i])
            maxt=t[i];
    }
    printf("%d\n",maxd);
    printf("%d\n",maxt);
    return 0;
}

2.//p1091 合唱队形

NOIP 2004 提高组 复赛 chorus 合唱队形

1.读完题目,可以理解为处理最长上升子序列,最长下降子序列。

2.上述思路闪过后,开始模拟样例,看看能不能整合思路。

3.顺序的上升子序列,逆序的上升子序列。

4.模拟了样例,如下:

5.思路如上,顺序上升,逆序上升,对应位置求和,找出最大值,总数-最大值+1即为答案。

6.开始编码。样例测试通过,提交AC。一次性成功。

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int a[100+10],b[100+10],c[100+10];
int max=1;
int main(){
    int n,i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<=n;i++){//顺序,最长上升子序列
        b[i]=1;
        for(j=1;j<i;j++)
            if(a[j]<a[i]){
                if(b[j]+1>b[i])
                    b[i]=b[j]+1;
            }
    }
    for(i=n;i>=1;i--){//逆序,最长上升子序列
        c[i]=1;
        for(j=n;j>i;j--)
            if(a[j]<a[i])
                if(c[j]+1>c[i])
                    c[i]=c[j]+1;
    }
    for(i=1;i<=n;i++)
        if(max<b[i]+c[i])
            max=b[i]+c[i];
    printf("%d\n",n-max+1);
}

2017-4-22 15:10

3.//p1280 尼克的任务
//样例模拟:
//1 2
//1 6
//4 14
//8 12
//8 8
//11 15
//1 6 8 12
//空闲时间7 13 14 15
//想了两天,毫无进展,看看他人是怎么想的吧。
//https://wenku.baidu.com/view/09d4e5a7284ac850ad02421f.html 写得不错,能较好帮组理解。
//http://blog.csdn.net/clove_unique/article/details/50448940 也写得不错。
//有一点要说明,该题数据已经排好序。
#include <stdio.h>
#include <string.h>
int p[10000+100],t[10000+100],f[10000+10];
int main(){
    int n,k,i,j;
    memset(f,0,sizeof(0));
    scanf("%d%d",&n,&k);
    for(i=1;i<=k;i++)
        scanf("%d%d",&p[i],&t[i]);
    j=k;
    for(i=n;i>=1;i--)
        if(i!=p[j])
            f[i]=f[i+1]+1;
        else
            while(i==p[j]){
                if(f[i]<f[p[j]+t[j]])
                    f[i]=f[p[j]+t[j]];
                j--;
            }
    printf("%d\n",f[1]);
    return 0;
}

2017-4-23 21:15

4.

5.

6.

多维动态规划

1.//P1508 Likecloud-吃、吃、吃
//看完提目,没什么感觉,只吃自己前方或左前方或右前方,搞不清楚
//翻看他人做法,说是 数字三角形变形。
//每组数据的出发点都是最后一行的中间位置的下方,理解了会,才明白,初始位置是在数字之外。
//采用了一个技巧a[0][1...m]=0,a[1...n][0]=0。省去了边界的处理。
#include <stdio.h>
#include <string.h>
int a[200+10][200+10],f[200+10][200+10];
int max1(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int max2(int a,int b,int c){
    if(a>max1(b,c))
        return a;
    else
        return max1(b,c);
}
int main(){
    int m,n,i,j,r,c;
    scanf("%d%d",&n,&m);
    memset(a,0,sizeof(a));
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            f[i][j]=max2(f[i-1][j-1],f[i-1][j],f[i-1][j+1])+a[i][j];
    r=n;
    c=(1+m)/2;
    printf("%d\n",max2(f[r][c-1],f[r][c],f[r][c+1]));
    return 0;
}
 

2.//P1006 传纸条

NOIP 2008 提高组 复赛 message 传字条

1.样例很快模拟成功,但感觉是凑出来的,没有章法。

2.深度优先遍历,但感觉容易超时。

3.动态规划?翻看他人代码,发现动态规划的写法,确实想不到,那怎么办,学呗。

4.以下两篇文章写得不错http://www.cnblogs.com/kiritoghy/p/4689791.html和http://www.cnblogs.com/sajuuk/p/4693249.html

5.//P1006 传纸条
//http://www.myexception.cn/program/1837942.html分析得相当精彩,而且详细具体。
//加入自己理解,尤其在点有交叉处,自认为编写得不错。
#include <stdio.h>
#include <string.h>
int a[55][55],f[55][55][55][55];
int max(int a,int b,int c,int d){
    if(a>=b&&a>=c&&a>=d)
        return a;
    if(b>=a&&b>=c&&b>=d)
        return b;
    if(c>=a&&c>=b&&c>=d)
        return c;
    if(d>=a&&d>=b&&d>=c)
        return d;
}
int main(){
    int i,j,k,l,m,n;
    memset(a,0,sizeof(a));
    memset(f,0,sizeof(f));
    scanf("%d%d",&m,&n);
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            scanf("%d",&a[i][j]);
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            for(k=1;k<=m;k++)
                for(l=1;l<=n;l++){//最后一个点
                    if(i==k&&j==l&&i==m&&j==n)
                        f[i][j][k][l]=max(f[i-1][j][k-1][l],f[i-1][j][k][l-1],f[i][j-1][k-1][l],f[i][j-1][k][l-1])+a[i][j];
                    else if(i==k&&j==l)//点重复,跳过
                        continue;
                    else
                        f[i][j][k][l]=max(f[i-1][j][k-1][l],f[i-1][j][k][l-1],f[i][j-1][k-1][l],f[i][j-1][k][l-1])+a[i][j]+a[k][l];
                }
    printf("%d\n",f[m][n][m][n]);
    return 0;
}

3.//P1387 最大正方形
//同样,没有什么太好的思路,翻看他人代码,真是写得很神奇。
//用该方法,对样例模拟了一遍,发现弄懂了,也发现,今后还是能想得到的。http://blog.csdn.net/liangzihao1/article/details/54945722
//动态规划,目前是多做多练,多积累,厚积薄发。

//动态规划,第一确定状态,进行模拟,之后,第二确定状态转移方程,就容易多了。
#include <stdio.h>
#include <string.h>
int f[100+10][100+10];
int min(int a,int b,int c){
    if(a<=b&&a<=c)
        return a;
    if(b<=a&&b<=c)
        return b;
    if(c<=a&&c<=b)
        return c;
}
int main(){
    int i,j,n,m,v,ans=0;
    memset(f,0,sizeof(f));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++){
            scanf("%d",&v);
            if(v){
                f[i][j]=min(f[i-1][j],f[i][j-1],f[i-1][j-1])+1;
                if(ans<f[i][j])
                    ans=f[i][j];
            }
        }
    printf("%d\n",ans);
    return 0;
}
 

4.

5.

6.

更要技巧的动规与记忆化
1.//P1064 金明的预算方案

NOIP 2006 提高组 复赛 budget 金明的预算方案

1.题目中透着01背包问题,样例也模拟出来了:400*3+500*2=2200

2.如何选择主次,存在疑虑。

//p1064 金明的预算方案
//读完题目,有条件限制的01背包问题
//编编看,看是否能理清关系
//第一步,排序,按主件在前,附件在后的办法进行处理。
//写完代码,测试样例都无法通过,明白该题得寻求外界帮助。
//《新编全国青少年信息需竞赛培训教材(复赛篇)》虽是pascal,但里面关于该题的叙述还是很有参考价值
//重写代码。 采用一维数组
//该题编写经历了三天,01背包问题,有了长足的进步。
#include <stdio.h>
#include <string.h>
int f[3200+100];//此处写成 f[60+10]
int v[60+10][3],p[60+10][3];//0主件1附件1 2附件2
int fun(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
int main(){
    int N,m,i,j,a,b,c,n,v0,v1,v2,p0,p1,p2;
    memset(v,0,sizeof(v));
    scanf("%d%d",&N,&m);
    n=N/10;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        if(c==0)
            c=i;
        for(j=0;j<=2;j++)
            if(v[c][j]==0){//1此处写成 v[i][j]==0
                v[c][j]=a/10;//缩小10倍
                p[c][j]=v[c][j]*b;
                break;//4漏了break,调了好长时间。是跟踪了代码,才发现此处有问题。
            }
    }
    
    for(i=0;i<=n;i++)
        f[i]=0;
    for(i=1;i<=m;i++)
        for(j=n;j>=0;j--)
            if(v[i][0]>0){//2漏了此条件
                v0=v[i][0];
                v1=v[i][1];
                v2=v[i][2];
                p0=p[i][0];
                p1=p[i][1];
                p2=p[i][2];
                if(j>=v0)
                    f[j]=fun(f[j],f[j-v0]+p0);
                if(j>=v0+v1)
                    f[j]=fun(f[j],f[j-v0-v1]+p0+p1);
                if(j>=v0+v2)//3漏了此种情况
                    f[j]=fun(f[j],f[j-v0-v2]+p0+p2);
                if(j>=v0+v1+v2)
                    f[j]=fun(f[j],f[j-v0-v1-v2]+p0+p1+p2);
            }
    printf("%d\n",f[n]*10);
}

2017-4-19 22:20

2.

3.

4.//P1063 能量项链

NOIP2006 提高组 复赛 energy 能量项链

1.读完题目,挺简单的,开个结构体,存储节点的头尾信息。

2.读取数据,记录头尾信息。

3.按顺序进行珠子能量合并,找出最大能量值。

4.提交10分,反复读题,觉得没问题,又觉得有问题,想了想,如果数据给定比较合适,珠子的合并顺序,可以不依照输入顺序进行合并,那该怎么处理呢,好像以目前的知识无能为力啊。

5.上网搜索,果然,说是动态规划问题,那就从头学吧。

6.学习http://wenku.baidu.com/link?url=UDL6fMzYuoXAl35TVnazjoGCIzZ3jUOtITvlitwybrhYNczdnsHMmYFJXb1e1ilxRFbfBdTYEqpcDjB3-gQ0Pf6nF_wItx9JyWKnctAAYsO动态规划:从新手到专家http://blog.csdn.NET/hzj379805931/article/details/51050741

附上数硬币对应的代码,根据文中伪代码,第一次用动态规划思想编写程序,很是高兴:

//dp count_coin 非递归
#include <stdio.h>
int v[3]={1,3,5};//币值种类
int min[1000000];
const int inf=999999;
int main(){
    int s;//总金额
    int i,j;
    scanf("%d",&s);//输入总金额
    min[0]=0;
    for(i=1;i<=s;i++)
        min[i]=inf;
    for(i=1;i<=s;i++)
        for(j=0;j<3;j++)
            if(v[j]<=i&&min[i-v[j]]+1<min[i])
                min[i]=min[i-v[j]]+1;
    printf("min[%d]=%d\n",s,min[s]);//输出构成总金额s的最少硬币个数
}

//dp count_coin2 递归
#include <stdio.h>
int v[3]={1,3,5};
int fun(int s){
    int i;
    int min=99999;
    if(s==0)
        return 0;
    for(i=0;i<3;i++)
        if(s>=v[i]&&fun(s-v[i])+1<min)
            min=fun(s-v[i])+1;
    return min;
}
int main(){
    int s;
    scanf("%d",&s);
    printf("%d\n",fun(s));
    return 0;
}

附上最长非降子序列的长度代码(我们定义d(i),表示前i个数中以A[i]结尾的最长非降子序列的长度。

//longest increasing subsqeuence 非递归
#include <stdio.h>
int main(){
    int a[]={5,3,4,8,6,7};
    int n=6;
    int i,j;
    int d[6];
    int maxLen;
    for(i=0;i<n;i++){
        maxLen=1;
        for(j=0;j<i;j++){
            d[i]=maxLen;
            if(a[j]<=a[i]&&d[j]+1>d[i])
                d[i]=d[j]+1;
            if(d[i]>maxLen)
                maxLen=d[i];
        }
        d[i]=maxLen;
    }
    printf("%d\n",d[5]);
    return 0;
}

//longest increasing subsequence 递归
#include <stdio.h>
int a[]={5,3,4,8,6,7};
int lis(int n){
    int i,j;
    int maxLen;
    if(n==0)
        return 1;
    maxLen=1;
    for(i=0;i<n;i++)
        if(a[i]<=a[n]&&lis(i)+1>maxLen)
            maxLen=lis(i)+1;
    return maxLen;
}
int main(){
    printf("%d\n",lis(6-1));
}
找到一篇好文,http://blog.csdn.Net/qq632544991p/article/details/52434951能将上述内容补齐

松鼠吃苹果,介绍得不错,但输出结果有误。又找了一个http://www.cnblogs.com/zhezh/p/3773284.html

附上苹果代码

//apple 非递归
#include <stdio.h>
int a[100][100];
int s[100][100];
int main(){
    int i,j;
    int row,col;
    scanf("%d%d",&row,&col);
    for(i=0;i<row;i++)
        for(j=0;j<col;j++)
            scanf("%d",&a[i][j]);
    //初始化,第一行,第一列
    s[0][0]=a[0][0];
    for(j=1;j<col;j++)//第一行
        s[0][j]=s[0][j-1]+a[0][j];
    for(i=1;i<row;i++)//第一列
        s[i][0]=s[i-1][0]+a[i][0];
    
    for(i=1;i<row;i++)
        for(j=1;j<col;j++)
            if(s[i][j-1]>s[i-1][j])
                s[i][j]=s[i][j-1]+a[i][j];
            else
                s[i][j]=s[i-1][j]+a[i][j];
    for(i=0;i<row;i++){
        for(j=0;j<col;j++)
            printf("%d\t",s[i][j]);
        printf("\n");
    }
    return 0;
}
输入数据:

3 3
1 2 3
4 5 6
7 8 9
 

输出数据:

1    3    6    
5    10    16    
12    20    29  

//apple 递归
#include <stdio.h>
int a[100][100];
int s[100][100];

int makes(int r,int c){
    if(r==0&&c==0)//第一个数据
        s[r][c]=a[r][c];
    else if(r==0)//第一列
        s[r][c]=makes(r,c-1)+a[r][c];
    else if(c==0)//第一行
        s[r][c]=makes(r-1,c)+a[r][c];
    else{
        if(makes(r,c-1)>makes(r-1,c))
            s[r][c]=makes(r,c-1)+a[r][c];
        else
            s[r][c]=makes(r-1,c)+a[r][c];
    }
    return s[r][c];
}
int main(){
    int i,j;
    int row,col;
    scanf("%d%d",&row,&col);
    for(i=0;i<row;i++)
        for(j=0;j<col;j++)
            scanf("%d",&a[i][j]);
    s[row-1][col-1]=makes(row-1,col-1);
    for(i=0;i<row;i++){
        for(j=0;j<col;j++)
            printf("%d\t",s[i][j]);
        printf("\n");
    }
    return 0;
}

能量项链,这几篇文章介绍得不错:

http://blog.sina.com.cn/s/blog_4c396f4301000bol.html

http://blog.csdn.net/a351357741/article/details/6493945

http://blog.sina.com.cn/s/blog_963453200101ketm.html

http://www.cnblogs.com/CYWer/p/4778720.html

着重http://blog.sina.com.cn/s/blog_963453200101ketm.html,可惜程序看不懂,怎么办,祭出跟踪大法,弄明白了,

第一重循环确定,聚合能量珠子总个数

第二重循环确定,第一个聚合珠子的起点位置

第三重循环确定,在起点珠子与终点珠子之间剖开位置。

开始编码,希望能成功。

附上AC代码,编译环境Dev-C++4.9.9.2

//2006 energy3
#include <stdio.h>
#include <string.h>
int e[200+20][200+20];
int a[200+20];
int my_max(int a,int b){
    if(a>=b)
        return a;
    else
        return b;
}
int main(){
    int n;
    int i,j,k;
    int t,max_e;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[n+i]=a[i];
    }
    memset(e,0,sizeof(e));//别忘了初始化,e[i][i]=0;
    for(j=1;j<=n-1;j++){//聚合珠子个数j+1
        for(i=1;i+j<=2*n-1;i++){//聚合珠子起点序号
            t=0;
            for(k=0;k<=j-1;k++){//聚合珠子剖开位置
                t=my_max(t,e[i][i+k]+e[i+k+1][i+j]+a[i]*a[i+k+1]*a[i+j+1]);//a[i]容易写成a[i+k]
            }
            e[i][i+j]=t;//此处容易犯错,e[i][j];
        }
    }
    max_e=0;
    for(i=1;i<=n;i++)
        max_e=my_max(max_e,e[i][i+n-1]);
    printf("%d\n",max_e);
    return 0;
}
很明显该题的动态归化难在写代码,不过认清珠子聚合至少两个,至多n个,作为第一重循环,此题就不难了。
2017-1-5 20:04

附上10分代码,编译环境Dev-C++4.9.9.2

//2006 energy 能量项链
#include <stdio.h>
struct node{
    int head;
    int tail;
}d[100+10];
int main(){
    int i,j;
    int n;
    int v;
    int max=-1;
    int e;
    int newhead,newtail;
    scanf("%d",&n);
    for(i=0;i<n;i++){
        scanf("%d",&v);
        d[i].head=v;
    }
    d[n-1].tail=d[0].head;
    for(i=0;i<n-1;i++)
        d[i].tail=d[i+1].head;
    for(i=0;i<n;i++){
        e=0;
        newhead=d[i].head;
        newtail=d[i].tail;
        for(j=i+1;j<n+i;j++){
            e+=newhead*newtail*d[j%n].tail;
            newtail=d[j%n].tail;
        }
        if(max<e)
            max=e;
    }
    printf("%d\n",max);
    return 0;
}

5.

6.P1052 过河

NOIP 2005 提高组 复赛 river 过河

1.样例模拟了,发现无论是从前到后,还是从后到前,都有多种可能。

2.当然,程序没什么头绪,选什么为状态?

3.对路径压缩心存疑虑,看了这篇,https://zhidao.baidu.com/question/137697462.html感觉好多了,摘抄如下:

以前做过,我证明一下:好像ST最大值分别为10,那么我们尽量让ST不重合,也就是S=9,T=10,来看一下,下面是可以
跳到的序列:
0, 9,10, 18,19,20, 27,28,29,30, 36,37,38,39,40 ……
90,91,92,93,94,95,96,97,98,99,100.
注意到在前面会有断开的区间,也就是状态不同,而81以后青蛙可以调到任何一个格,所以可以压缩到100.至于为什么
不压缩到更低,是因为前面那些便于你理解,实际上是这个公式算的:lcm(max(T),max(T)-1)证明复杂从略。

4.https://zhidao.baidu.com/question/69031147.html也讲得不错。

摘抄如下:

动态转移方程很简单,主要是状态压缩

如果两块石头A和B,AB的距离超过2520,则让B及其后的石头的位置每次减2520,直到AB的距离小于2520.(PS:2520为1-10的最小公倍数)
这样一来路径就能减小为260000以下.
然后DP每个点就OK了.也不用考虑S=T的情况.程序极短
几个注意点:
1.若最后一个石头的位置离终点很远,那么将终点位置提前,方法一样.
2.有些地方不能到达,则可以将其石头数顶为101,这样S=T也不会错.

其实如果不考虑s=t的话,两点大于100的可以压到100,甚至20,某人用压到10仅错了1组数据。。。

5.http://blog.csdn.NET/zzp441524586/article/details/7654269讲得很棒

石子稀疏对我们解题有什么帮助呢?我们来看一下下面的推断:

第一种情况:      

      当s=t时,很简单,青蛙踩到的石头肯定是s的倍数,那么只要统计一下所有石子中有多少是s的倍数,输出即可。

第二种情况:s<t

我们先来看一组数据。s=4,t=5。

从数据中我们看到,12以后的点全部都是可以到达的了。如果s=4,t=5,在一段100000的距离中没有石头,其实12以后的点都是不用递推就知道肯定能到达的。那么我们用原始的方法做就会浪费很大的资源。

        所以当s=4,t=5时,如果一段没有石头的区间长度在4*5=20以外,那么我们只要递推前20就可以了,因为20后面的情况是一样的(仔细想想为什么?)。

假设s=3,t=5,那么11=3+4+4就也可以到达了。所以,只有当t=s+1时,连续的点的起始位置才能尽量后面。最坏情况就是s=9,t=10了(仔细想想为什么?)。

       跟前面的s=4,t=5的情况一样,其实s=9,t=10时只要一段没有石头的区间长度在90之外,我们都把它当做90对待就可以了。

        因此,我们每次对于一段没有石头的区间长度为x,如果x<=t(t-1),我们仍然把它当做x来处理;相反,当x>t(t-1)时,我们就把它当做t(t-1)处理。这样,最大的复杂度就是t(t-1)*(石头个数+1)=90*101=9090,比之前的复杂度大大降低。

        这种方法叫状态压缩,我们这题用的方法叫离散化。至此,过河完美AC!

6.有了上面的基础后,那么这篇文章就容易看懂了,http://blog.csdn.net/yuyanggo/article/details/48341259摘抄如下:

桥很长,但是石子数很少,也就是说,中间可能存在很长的一段空白区域,而这段空白区域就是造成大量无效运算的元凶,需要我们将这部分空白区域进行压缩。

         现在,我们假设每次走p或者p+1步,则有 px+(p+1)y=s。

         1.gcd(p+1,p)=gcd(1,p)=1,即p与p+1的最大公约数是1;

         2.由扩展欧几里得可知,对于二元一次方程组:px+(p+1)y==gcd(p,p+1)是有整数解的,即可得:

           px+(p+1)y==s是一定有整数解的。

         假设px+(p+1)y==s的解为:x=x0+(p+1)t,y=y0-pt。令0<=x<=p(通过增减t个p+1来实现),s>p*(p+1)-1,则有:y=(s-px)/(p+1)>=(s-p*p)/(P+1)>(p*(p+1)-1-px)/(p+1)>-1>=0

          即表示,当s>=p*(p+1)时,px+(p+1)y==s有两个非负整数解,每次走p步或者p+1步,p*(p+1)之后的地方均能够到达。如果两个石子之间的距离大于p*(p+1),那么就可以直接将他们之间的距离更改为p*(p+1)。

          综上,得到压缩路径的方法:若两个石子之间的距离>t*(t-1),则将他们的距离更改为t*(t-1)。

7.//p1052 过河
//提交,测试点2,6 RE,测试点3,7,10 WA
//for(i=1;i<=m;i++)//1 此处漏了循环,提交测试点2,6Re 测试点7WA
//#define maxn 110//2 此处写成 #define maxn 100+10 提交AC
#include <stdio.h>
#include <string.h>
#define maxn 110//2 此处写成 #define maxn 100+10
#define inf 999999999
int a[maxn],f[maxn*maxn],stone[maxn*maxn];
int min(int a,int b){
    if(a>b)
        return b;
    return a;
}
int main(){
    int l,s,t,m,i,j=0,d,k,ans=0,b;
    memset(a,0,sizeof(a));
    for(i=0;i<maxn*maxn;i++)
        f[i]=inf;
    memset(stone,0,sizeof(stone));
    scanf("%d%d%d%d",&l,&s,&t,&m);
    for(i=1;i<=m;i++)
        scanf("%d",&a[i]);
    if(s==t){
        for(i=1;i<=m;i++)//1 此处漏了循环
            if(a[i]%s==0)
                ans++;
        printf("%d\n",ans);
        return 0;
    }
    for(i=1;i<=m;i++)//自小到大
        for(j=i+1;j<=m;j++)
            if(a[i]>a[j]){
                b=a[i];
                a[i]=a[j];
                a[j]=b;
            }
    k=s*t;
    for(j=0,i=1;i<=m;i++){
        a[i]-=j;
        d=a[i]-a[i-1];
        if(d>k){
            a[i]=a[i-1]+k;
            j+=d-k;
        }
    }
    for(i=1;i<=m;i++)
        stone[a[i]]=1;
    f[0]=0;
    for(i=0;i<=a[m];i++)
        for(j=s;j<=t;j++)
            if(stone[i+j]==1)
                f[i+j]=min(f[i+j],f[i]+1);
            else
                f[i+j]=min(f[i+j],f[i]);
    ans=inf;
    for(i=1;i<t;i++)
        ans=min(ans,f[a[m]+i]);
    printf("%d\n",ans);
    return 0;
}

2017-5-11 23:50

高精度度算法

1.//p1601 A+B Problem(高精)
//基本思路:字符串读取
//之后逆序存储,方便加法对齐
//设置进位 整数数组,方便计算。
//笔误不断,祭出跟踪大法,总算排除万难。 

附上常见写法:2017-7-9

//P1601 A+B Problem(高精)
#include <stdio.h>
#include <string.h>
#define maxn 500+10
int x[maxn],y[maxn],z[maxn];
char a[maxn],b[maxn];
int add(int *x,int *y,int *z){//x+y=z
    int i;
    z[0]=x[0]>y[0]?x[0]:y[0];
    for(i=1;i<=z[0];i++){
        z[i]+=x[i]+y[i];
        z[i+1]=z[i]/10;
        z[i]%=10;
    }
    if(z[i]>0)
        z[0]=i;
}
int main(){
    int i,len;
    memset(z,0,sizeof(z));
    scanf("%s%s",a,b);
    len=strlen(a);
    x[0]=len;
    for(i=len-1;i>=0;i--)
        x[len-i]=a[i]-'0';//1 此处写成x[i-(len-1)+1] 
    len=strlen(b);
    y[0]=len;
    for(i=len-1;i>=0;i--)
        y[len-i]=b[i]-'0';//1 此处写成y[i-(len-1)+1] 
    add(x,y,z);
    for(i=z[0];i>=1;i--)//2 此处写成 for(i=1;i<=z[0];i++)
        printf("%d",z[i]);
    return 0;
}

下面代码,为初次接触时的代码:
#include <stdio.h>
#include <string.h>
char a[600],b[600],t[600];
int g[600],c[600],lenc;//g进位处理,c和结果
void add(char *a,char *b){
    int i,j,lena,lenb,len,u,v;
    lena=strlen(a);
    lenb=strlen(b);
    for(i=0;i<lena;i++){//逆序
        t[i]=a[lena-1-i];
    }
    t[lena]='\0';
    strcpy(a,t);
    
    for(i=0;i<lenb;i++){//逆序
        t[i]=b[lenb-1-i];//3此处写成t[i]=a[lenb-1-i];,查了好长时间,笔误啊
    }
    t[lenb]='\0';
    strcpy(b,t);
    len=lena<lenb?lena:lenb;//取最短长度
    for(i=0;i<len;i++){
        u=a[i]-'0';
        v=b[i]-'0';
        c[i]=(u+v+g[i])%10;//当前位置结果
        g[i+1]=(u+v+g[i])/10;//进位
    }
    if(lena<lenb)//记录最长字符串
        strcpy(t,b);
    else
        strcpy(t,a);
    len=lena<lenb?lenb:lena;//取最长长度
    for(j=i;j<len;j++){
        u=t[j]-'0';
        c[j]=(u+g[j])%10;//1此处写成c[j]=(u+g[i])%10;,查了会
        g[j+1]=(u+g[j])/10;//1此处写成g[j+1]=(u+g[i])/10;,查了会
    }
    if(g[j]>0){
        c[j]=g[j];
        lenc=j+1;
    }else
        lenc=j;
}
int main(){
    int i;
    memset(g,0,sizeof(g));
    scanf("%s%s",a,b);
    add(a,b);
    for(i=lenc-1;i>=0;i--)//2此处写成for(i=0;i<lenc;i++),查了会,应逆序输出
        printf("%d",c[i]);
    printf("\n");
    return 0;
}

2.//p2142 高精度减法
//题目未说明字符串长度,故按p1601 A+B Problem(高精)字符数组开到a[600]
//题目未说明,但本人编写只考虑两个非负整数减法。
//编写比较函数必不可少。
//头部处理,要注意 如 100-9=091打印91
//编写下来,小错误不断,跟踪必不可少。
#include <stdio.h>
#include <string.h>
char a[600],b[600],t[600];
int c[600],g[600],lenc;//c结果,g退位处理
int cmp(char *a,char *b){//比大小,a>b大于0 a<b小于0 a==b等于0
    int lena,lenb,i,j;
    lena=strlen(a);
    lenb=strlen(b);
    if(lena>lenb)//a>b
        return 1;
    if(lena<lenb)//a<b
        return -1;
    if(lena==lenb){
        for(i=0;i<lena;i++){//3漏了前后两个大括号,查了会
            if(a[i]>b[i])//a>b
                return 1;
            if(a[i]<b[i])//a<b
                return -1;
        }
        return 0;//a==b
    }
}
void sub(char *a,char *b){//a>=b
    int i,j,lena,lenb,u,v;
    
    if(cmp(a,b)==0){//相等
        lenc=1;
        return;
    }
    
    lena=strlen(a);//1漏写该句,查了会
    lenb=strlen(b);//1漏写该句,查了会
    //逆序存储
    for(i=0;i<lena;i++)
        t[i]=a[lena-1-i];
    t[i]='\0';
    strcpy(a,t);
    
    for(i=0;i<lenb;i++)
        t[i]=b[lenb-1-i];
    t[i]='\0';
    strcpy(b,t);
    
    
    for(i=0;i<lenb;i++){
        u=a[i]-'0';
        v=b[i]-'0';
        if(u<v+g[i]){//2此处写成 a[i]<b[i]+g[i],查了会,请注意字符转数字
            c[i]=u+10-v-g[i];
            g[i+1]=1;//退位
        }else
            c[i]=u-v-g[i];
    }

    for(i=lenb;i<lena;i++){
        u=a[i]-'0';
        if(u<g[i]){
            c[i]=u+10-g[i];
            g[i+1]=1;
        }else
            c[i]=u-g[i];
    }
    //头部0的处理 如 100-9=81会少位数
    i=lena-1;
    while(c[i]==0)
        i--;
    lenc=i+1;
}
int main(){
    int i;
    memset(c,0,sizeof(c));
    memset(g,0,sizeof(g));
    scanf("%s%s",a,b);
    if(cmp(a,b)==-1)//打印负号
        printf("-");
    //计算结果的绝对值一定是大减小。
    if(cmp(a,b)==-1)
        sub(b,a);//a<b
    else
        sub(a,b);//a>=b
    for(i=lenc-1;i>=0;i--)
        printf("%d",c[i]);
    return 0;
}

3.//p1303 A*B Problem
//题目未说明字符串长度,故按p1601 A+B Problem(高精)字符数组开到a[600]
//题目未说明,但本人编写只考虑两个非负整数乘法。
//提交60分,测试点4 WA 测试点5 RE
//看了他人提示,将数组从600开到1000,提交80分 测试点5 RE
//继续将数组从1000开到10000,提交AC。莫名其妙,竟然出题不写数据范围。
#include <stdio.h>
#include <string.h>
char a[10000],b[10000],t[10000];
int c[10000],g[10000],lenc;//c积 g进位
void mul(char *a,char *b){//假定长度lena>lenb
    int i,j,lena,lenb,u,v;
    lena=strlen(a);
    lenb=strlen(b);
    //逆序存储
    for(i=0;i<lena;i++)
       t[i]=a[lena-1-i];
    t[i]='\0';
    strcpy(a,t);
    
    for(i=0;i<lenb;i++)
       t[i]=b[lenb-1-i];
    t[i]='\0';
    strcpy(b,t);
    for(i=0;i<lenb;i++){
        u=b[i]-'0';//1此处写成u=a[i]-'0';,查了会
        memset(g,0,sizeof(g));//别忘了,进位每次要清0
        for(j=0;j<lena;j++){
            v=a[j]-'0';//1此处写成v=b[j]-'0';,查了会
            g[i+j+1]=(u*v+g[i+j]+c[i+j])/10;//进位 2调换了两句的前后位置,查了会  之前位置:后
            c[i+j]=(u*v+g[i+j]+c[i+j])%10;//积  2调换了两句的前后位置,查了会 之前位置 前
        }
        c[i+j]=g[i+j];
    }
    
    i=lena+lenb-1;
    while(c[i]==0&&i>=0)
        i--;
    if(i<0)//乘0处理
        lenc=1;
    else
        lenc=i+1;
}
int main(){
    int i,j;
    memset(g,0,sizeof(g));
    memset(c,0,sizeof(c));
    scanf("%s%s",a,b);
    if(strlen(a)>strlen(b))
        mul(a,b);
    else
        mul(b,a);
    for(i=lenc-1;i>=0;i--)
        printf("%d",c[i]);
    printf("\n");
    return 0;
}

2017-4-13

BOSS战-普及综合练习1

1.//p1478 陶陶摘苹果(升级版)
//难度:普及-
//考点:输入,输出 ,数组,结构体,排序  
//适用:小学生
//注意:存在力气用不完,苹果已摘完;存在力气不够用苹果已摘完。
//思路:找出能摘到的苹果,按消耗的力气由小到大排序。第一遍扫描,将能摘到的苹果存下来,采用结构体,数据比较清晰。

#include <stdio.h>
struct node{
    int x;
    int y;
}p[5000+10],p_t;//苹果 

int main(){
    int n,s;
    int i,j;
    int x,y;
    int a,b;
    int count=0;
    int num=0;
    scanf("%d%d",&n,&s);
    scanf("%d%d",&a,&b);
    for(i=0;i<n;i++){
        scanf("%d%d",&x,&y);
        if(a+b>=x){//存能摘到的苹果 
            p[count].x=x;
            p[count].y=y;
            count++;
        }
    }
    for(i=0;i<count;i++)//自小到大排序 
        for(j=i+1;j<count;j++)
            if(p[i].y>p[j].y){
                p_t=p[i];
                p[i]=p[j];
                p[j]=p_t;
                
            }
    for(i=0;i<count;i++)
        if(s>=p[i].y){
            s=s-p[i].y;
            num++;
        }else
            break; 
    printf("%d\n",num);
    return 0;
}

2.//P1203 [USACO1.1]坏掉的项链Broken Necklace
//自个想法是枚举,以为他人会有什么好办法,翻看他人,看了描述部分几个字,枚举,代码没看。
//那么好吧,自己动手,丰衣足食。
//仔细想想,做个关于'w'的预处理,写起代码来会更省心。一写,代码量还挺大,决定推倒重来。
//编着编着,分'w'非'w'挺复杂的,翻看这篇http://blog.csdn.net/chai_jing/article/details/53103002发现确实写得好.
//试了上文,无法AC。但不影响算法写得好。错了测试点3
//考虑两个问题,一是超过n长度,直接输出n,二是'w'开头,可以看成'r'或'b',这个题意不清。提交AC
//该题思路比较奇特,对于按部就班,分类讨论的是极大的挑战。
//技巧与 NOIP 2006 提高组 复赛 能量项链 有部分相同
#include <stdio.h>
char a[800];
int n;
int right(int i){
    int ans=0,j;
    for(j=i;j<=i+n-1;j++)//右扫
        if(a[i]==a[j]||a[j]=='w')
            ans++;
        else
            break;
    return ans;
}
int left(int i){
    int ans=0,j;
    for(j=i+n-1;j>=i;j--)//左扫
        if(a[i+n-1]==a[j]||a[j]=='w')
            ans++;
        else
            break;
    return ans;
}
int main(){
    int i,j,max=0,ans,b,c,d;
    
    scanf("%d%s",&n,a+1);
    for(i=1;i<=n;i++)
        a[i+n]=a[i];//拼成2n串,无需取模运算。
    for(i=1;i<=n;i++){
        ans=0;
        if(a[i]=='w'){//右扫处理
            a[i]='r';
            b=right(i);
            a[i]='b';
            c=right(i);
            a[i]='w';//恢复数据
            d=b>c?b:c;
        }else{
            d=right(i);
        }
        ans+=d;
        if(a[i+n-1]=='w'){//左扫处理
            a[i+n-1]='r';
            b=left(i);
            a[i+n-1]='b';
            c=left(i);
            a[i+n-1]='w';//恢复数据
            d=b>c?b:c;
        }else{
            d=left(i);
        }
        ans+=d;
        if(max<ans)
            max=ans;
    }
    if(max>n)
        printf("%d\n",n);
    else
        printf("%d\n",max);
    return 0;
}

3.

4.

NOIP 2006 提高组 复赛 digital 2k 进制数

//洛谷 P1066 2^k进制数
//提示:作为结果的正整数可能很大,但不会超过200位,考虑用高精度算法,加
//没什么思路,翻看他人做法,积累经验,该题还考什么,排列组合,猜测。
//http://hzwer.com/794.html此文写得不错,同时,也应证了本人的想法。
//自个举了个例子3 8
//2位 C[7][2]=21
//3位 首位最大值(11)2 (3)10 C[6][2]+c[5][2]+c[4][2]=31
//首位放1 C[6][2]  2 c[5][2]  3 c[4][2]
//组合数公式,可以由杨辉三角而来,c[i][j]=c[i-1][j]+c[i-1][j-1];
//突然意识到,用高精度算法的程序,测试起来极其困难。但还是找到了测试办法,不打印测试数据,打印要计算的数据
//提交,全报MLE,没辙,将 int c[520][520][210];改成int c[520][520][100];
//c[0][0][0]=1,c[0][0][1]=0;//更改此句memset(c,0,sizeof(c));算法效率立马得到了极大的提高,由2637ms提高到45ms
//该题疑问,数组最大能开到多大,或者说内存大小如何计算。猜测10^7到10^8之间,待日后查证
//该题耗时4天,主要时间花在理解 除首位计数情况,包含首位计数情况。2017-5-17 AC
#include <stdio.h>
#include <string.h>
int c[520][520][100];//标准是512 512 200现在安排有一定弹性空间,隐忧,容易内存溢出
int ans[210];
void plus1(int x[],int y[],int z[]){//3 z[0]存储数据长度,z[1]是最低位,打印注意,要从最高位打起
    int i;
    z[0]=x[0]>y[0]?x[0]:y[0];
    for(i=1;i<=z[0];i++){
        z[i]+=x[i]+y[i];
        z[i+1]=z[i]/10;//进位, 2 此处写成 z[i+1]=z[i]%10;
        z[i]%=10;//2 此处写成 z[i]/=10;  
    }
    if(z[z[0]+1]>0)
        z[0]++;//当前数据长度
}
void plus2(int x[],int y[]){
    int i;
    x[0]=x[0]>y[0]?x[0]:y[0];
    for(i=1;i<=x[0];i++){
        x[i]+=y[i];
        x[i+1]+=x[i]/10;//4 此处写成x[i+1]+=x[i]/10 进位,2 此处写成  x[i+1]=x[i]%10;
        x[i]%=10;//2 此处写成  x[i]/=10;
    }
    if(x[x[0]+1]>0)
        x[0]++;//当前数据长度
}
int main(){
    int k,w,b,h,i,j;
    scanf("%d%d",&k,&w);
    b=1<<k;
    h=1<<w%k;
    c[0][0][0]=1,c[0][0][1]=0;//更改此句memset(c,0,sizeof(c));算法效率立马得到了极大的提高,由2637ms提高到45ms
    memset(ans,0,sizeof(ans));
    for(i=1;i<b;i++)//打表,排列组合
        for(j=0;j<=i;j++)//1此处写成for(j=0;j<i;j++)
            if(j==0||i==j){//此句写得很棒,配合上c[0][0][0]=1,c[0][0][1]=0;对于 c[i][j]=c[i-1][j]+c[i-1][j-1];计算,无懈可击.自认为关于组合数的计算,这个写法是相当棒的。
                c[i][j][0]=1;//数据长度
                c[i][j][1]=1;//初始数据为1
            }else
                plus1(c[i-1][j],c[i-1][j-1],c[i][j]);
    for(i=2;i<=w/k&&i<b;i++)//除首位的情况 i<=w/k除首位情况,i<b若位数比较多,不能超越可选数字个数b-1
        plus2(ans,c[b-1][i]);
    for(i=1;i<h&&i+w/k<b;i++)//有首位的情况 i<h在首位里取1,2,...,h-1的情况,同时总需求个数i+w/k不能超越数字个数b-1
        plus2(ans,c[b-1-i][w/k]);//4 此处写成 plus2(ans,c[b-1-i][i]);
    for(i=ans[0];i>=1;i--)//3 z[0]存储数据长度,z[1]是最低位,打印注意,要从最高位打起 此处写成 for(i=1;i<=ans[0];i++)
        printf("%d",ans[i]);
    printf("\n");
    return 0;
}

BOSS战-普及综合练习2

1.//P1201 [USACO1.1]贪婪的送礼者Greedy Gift Givers
//读完题目,第一直觉,弄懂样例,该题就做了大半了。
//模拟了样例中dave的数据,发现剩下的钱可以算做收到的钱。
//开个结构体,虽然多耗费了空间,但理解起来比较方便。
//该题的输入也值得一编,有些烦,但不难。
//编着编着,突然发现,查找需要花时间,是否会超时?2000*2000=4*10^6应该不会超时
//提交,测试点1,7,8WA,测试点4,5,6,9TLE
//感觉题目不对劲,查了英文原题,
//Lines NP+2..end:     NP groups of lines organized like this:
//The first line in the group tells the person's name who will be giving gifts.
//The second line in the group contains two numbers: The initial amount of money (in the range 0..2000) to be divided up into gifts by the giver and then the number of people to whom the giver will give gifts, NGi (0 ≤ NGi ≤ NP-1).
//If NGi is nonzero, each of the next NGi lines lists the the name of a recipient of a gift.
//看了他人代码,真是火大了,怎么翻译的,弄了半天,发钱的种类同样有n种,关键一句: NP groups of lines organized like this:
//修改,提交AC,水题一道,翻译坏了事,真是服了洛谷对该题的翻译,水题变难题。
#include <stdio.h>
#include <string.h>
struct node{
    char name[20];
    int fa;
    int shou;
}q[2000+10];
int main(){
    int n,i,j,k,fa,num;
    char name[20];
    memset(q,0,sizeof(q));
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%s",&q[i].name);
    for(i=1;i<=n;i++){
        scanf("%s",name);
        scanf("%d%d",&fa,&num);
        for(j=1;j<=n;j++)
            if(strcmp(q[j].name,name)==0){
                q[j].fa=fa;
                if(num!=0)
                    q[j].shou+=fa%num;
                break;
            }
        for(j=1;j<=num;j++){
            scanf("%s",&name);
            for(k=1;k<=n;k++)
                if(strcmp(q[k].name,name)==0){
                    if(num!=0)
                        q[k].shou+=fa/num;//1 此处写成q[j].shou=fa/num;,查了会
                    break;
                }
        }
    }
    for(i=1;i<=n;i++)
        printf("%s %d\n",q[i].name,q[i].shou-q[i].fa);
    return 0;
}
 

2.

3.

4.

普及常见模板

1.

洛谷 P1177 【模板】快速排序

1.翻书,该题很容易解决,但不算掌握。

2.凭空编写,边界点的取值有些问题,等号去还是不取。

3.想了一个办法,写出一组数据进行手动模拟,弄明白了,程序再开始根据模拟进行编制。

4.很久没写快排了,如果能一次性编写成功,这次可以说快排掌握了。

5.开始动手,。

6.第一次取a[left]为中枢,得分40.

7.第二次取a[(left+right)/2]为中枢,得分60.

8.第三次取a[left],a[right],a[(left+right)/2]中间值为中枢,得分60.

9.本想上归并排序,但难度较大,时间较紧,只能作罢。

10.参考AC代码,进行模拟,发现一个很好的快排代码。

11.对着模拟,进行编码,修改小错误后,提交AC.

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int a[100000+100];
void quicksort(int left,int right){
    int i=left,j=right,t;
    int mid=a[(left+right)/2];//请注意,在处理的过程中a[(left+right)/2]的值是会变化的
    while(i<=j){//i,j的值是会跳跃的,循环结束时i>j
        while(a[i]<mid)
            i++;
        while(a[j]>mid)
            j--;
        if(i<=j){
            t=a[i];
            a[i]=a[j];
            a[j]=t;
            i++;
            j--;
        }
    }
    if(i<right)
        quicksort(i,right);
    if(left<j)
        quicksort(left,j);
    
}
int main(){
    int n;
    int i;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    quicksort(0,n-1);
    printf("%d",a[0]);
    for(i=1;i<n;i++)
        printf(" %d",a[i]);
    printf("\n");
    return 0;

}

//P1177 【模板】快速排序
//采用 归并排序 处理
//样例通过,提交AC 2018-1-2 17:23
#include <stdio.h>
int a[100100],b[100100];
void memery_sort(int left,int mid,int right){//自小到大
    int i=left,j=mid+1,n=mid,m=right,k=0;
    while(i<=n&&j<=m)
        if(a[i]>a[j])b[k++]=a[j++];
        else b[k++]=a[i++];
    while(i<=n)b[k++]=a[i++];
    while(j<=m)b[k++]=a[j++];
    for(i=0;i<k;i++)a[left+i]=b[i];//1此处写成 for(i=1;i<k;i++)a[i]=b[i];
}
void merge_sort(int left,int right){
    int mid=(left+right)/2;
    if(left>=right)return ;
    merge_sort(left,mid);
    merge_sort(mid+1,right);
    memery_sort(left,mid,right);
}
int main(){
    int n,i;
    scanf("%d",&n);
    for(i=0;i<n;i++)scanf("%d",&a[i]);
    merge_sort(0,n-1);
    for(i=0;i<n;i++)printf("%d ",a[i]);
    return 0;
}
 

2.//P3366 【模板】最小生成树
//按《啊哈!算法》P221思路进行编写,Prim(普里姆)算法,样例很快通过,提交,全WA
//下载测试点1, 跟踪,发现问题 if(vis[j]==0&&d[j]<min){//1此处写成 if(d[j]<min),查了会 ,提交AC
//没有优化过的程序,执行效率:耗时:1764ms 内存:104152kb  
#include <stdio.h>
#include <string.h>
#define maxn 5000+10
#define INF 999999999
int e[maxn][maxn],vis[maxn],d[maxn];//d[]生成树外节点到生成树的最短距离
int main(){
    int n,m,i,j,u,v,w,a=INF,k,min,sum=0;
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){//好久不打,邻接矩阵的初始化都忘记了。
            e[i][j]=INF;
            e[i][j]=INF;
        }
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);//可能有重边
        if(w<e[u][v]){//存在重边,取最小
            e[u][v]=w;//无向图
            e[v][u]=w;
        }
    }
    //生成树从点1开始,故统计生成树,从点2开始
    vis[1]=1;
    for(i=1;i<=n;i++)//初始化d[]值
        if(vis[i]==0)
            d[i]=e[1][i];
    for(i=1;i<=n-2;i++){//n-1条边,到1点的边上面已处理,故有n-2次处理
        min=INF;
        k=0;
        for(j=1;j<=n;j++)//找d[]中最小值
            if(vis[j]==0&&d[j]<min){//1此处写成 if(d[j]<min),查了会
                min=d[j];
                k=j;
            }
        vis[k]=1;//将k加入生成树
        for(j=1;j<=n;j++)
            if(vis[j]==0&&e[k][j]<d[j])
                d[j]=e[k][j];//生成树外节点到生成树的最短距离
    }
    for(i=2;i<=n;i++){
        if(d[i]==INF){            
            printf("orz\n",i);
            return 0;
        }
        sum+=d[i];
    }
    printf("%d\n",sum);
    return 0;
}

2017-6-14 15:35 AC

//P3366 【模板】最小生成树
//《啊哈!算法》P215对Kruskal算法有详细介绍
//有了并查集,快排的基础,程序很快编完,样例通过,提交AC。
//程序效率:耗时:231ms 内存:10671kb  
#include <stdio.h>
struct node{
    int u;
    int v;
    int w;
}e[200000+100],et;
int f[5000+100];
int getf(int u){//找爸爸
    if(f[u]==u)
        return f[u];
    f[u]=getf(f[u]);
    return f[u];
}
int merge(int u,int v){//合并两个集合,靠左
    int f1=getf(u),f2=getf(v);
    if(f1!=f2){
        f[f2]=f1;
        return 1;//不同集合,合并
    }
    return 0;//同一集合
}
void quicksort(int left,int right){//快速排序,自小到大
    int i=left,j=right,mid=e[(left+right)/2].w;
    while(i<=j){
        while(e[i].w<mid)
            i++;
        while(e[j].w>mid)
            j--;
        if(i<=j){
            et=e[i];
            e[i]=e[j];
            e[j]=et;
            i++;
            j--;
        }
    }
    if(left<j)
        quicksort(left,j);
    if(i<right)
        quicksort(i,right);
}
int main(){
    int i,n,m,u,v,w,count=0,sum=0;//count统计加入边的数目
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        e[i].u=u,e[i].v=v,e[i].w=w;
    }
    quicksort(1,m);
    for(i=1;i<=n;i++)
        f[i]=i;
    for(i=1;i<=m;i++){
        if(merge(e[i].u,e[i].v)==1){//两个不同集合
            sum+=e[i].w;
            count++;
            if(count==n-1)
                break;
        }
    }
    if(count==n-1)
        printf("%d\n",sum);
    else
        printf("orz\n");
    return 0;
}

 

3.//P3367 【模板】并查集
//《啊哈!算法》对并查集有过专门介绍,感觉手到擒来
//但也有隐忧,会不会超时?
//样例很快通过,提交AC,耗时,内存都比较少。
#include <stdio.h>
int f[10000+100];
int getf(int u){
    if(f[u]==u)
        return u;
    f[u]=getf(f[u]);
    return f[u];
}
void merge(int u,int v){//靠左
    int f1,f2;
    f1=getf(u);
    f2=getf(v);
    if(f1!=f2)
        f[f2]=f1;
}
int main(){
    int i,n,m,z,x,y,f1,f2;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        f[i]=i;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&z,&x,&y);
        if(z==1)
            merge(x,y);
        else if(z==2){
            f1=getf(x);
            f2=getf(y);
            if(f1==f2)
                printf("Y\n");
            else
                printf("N\n");
        }
    }
    return 0;
}
 

4.//P3371 【模板】单源最短路径
//《啊哈!算法》专门对Dijkstra算法进行了介绍,算法复杂度O(n^2),怀疑会超时
//先独立编写,再进行超时探讨。
//样例很快通过,提交,测试点1-7 MLE 测试点8-10 TLE。
//很明显,要改进算法。
//首先,采用邻接表来存储边,能将空间复杂度降下来。
//提交,测试点2,3,4,6WA 估计因该是 #define INF 999999999问题 修改成 #define INF 2147483647提交, 测试点2,3,4,6WA
//if(u[i]==s&&d[v[i]]>w[i])//1 此处 if(u[i]==s) 查了好久,提交AC,请注意,两点之间,直接可以有多条不同长度的路径,取最小值。
//额,数据有毒
//5 15 5 2 2 270
//1 4 89 2 1 3 5 5 261
//5 2 163
//5 5 275
//4 5 108
//4 4 231
//3 4 213
//3 3 119
//3 1 77
//3 1 6
//2 4 83
//5 5 196
//5 5 94 这么多从起点到终点一样的路是想干啥!!!!!
//而且,从那条路能到3号点!!!!!

#include <stdio.h>
#include <string.h>
#define INF 2147483647
int u[500000+100],v[500000+100],w[500000+100],first[10000+100],next[500000+100],d[10000+100],vis[10000+100];
int n,m,s;
int findmin(){//找剩下的最小值,返回点
    int i,k,min=INF;
    for(i=1;i<=n;i++)
        if(vis[i]==0&&d[i]<min){
            min=d[i];
            k=i;
        }
    return k;
}
int main(){
    int i,j,k;
    memset(vis,0,sizeof(vis));
    scanf("%d%d%d",&n,&m,&s);
    for(i=1;i<=n;i++)
        d[i]=INF;
    d[s]=0;
    vis[s]=1;
    memset(first,-1,sizeof(first));
    memset(next,-1,sizeof(next));
    for(i=1;i<=m;i++){//边读取
        scanf("%d%d%d",&u[i],&v[i],&w[i]);
        next[i]=first[u[i]];
        first[u[i]]=i;
        if(u[i]==s&&d[v[i]]>w[i])//1 此处 if(u[i]==s) 查了好久,提交AC,请注意,两点之间,直接可以有多条不同长度的路径,取最小值。
            d[v[i]]=w[i];
    }
    for(i=1;i<=n-1;i++){//n-1次循环
        k=findmin();
        vis[k]=1;
        j=first[k];//j是边
        while(j!=-1){
            if(vis[v[j]]==0&&d[k]+w[j]<d[v[j]])
                d[v[j]]=d[k]+w[j];
            j=next[j];
        }
    }
    printf("%d",d[1]);
    for(i=2;i<=n;i++)
        printf(" %d",d[i]);
    printf("\n");
    return 0;
}
 

5.//P3383 【模板】线性筛素数
//基本思路,先打一个素数表,无需每次输入都进行判定。
//提交测试点4,5,6,7,8,9,10 全TLE.
//又要学新东西啦。
//新代码,if( !(i % prime[j] ) ) break;确实很难理解,采用跟踪N=10,N=20,N=30,N=40观察数据
//再看此文描述http://blog.csdn.net/u014427196/article/details/44466461弄懂了
#include <stdio.h>
#include <string.h>
int a[10000000+10],prime[1000000];
int main(){
    int n,m,i,j,v,k=0;
    scanf("%d%d",&n,&m);
    memset(a,0,sizeof(a));
    a[0]=1,a[1]=1;//合数
    for(i=2;i<=n;i++){
        if(a[i]==0)
            prime[k++]=i;
        for(j=0;j<k&&i*prime[j]<=n;j++){
            a[i*prime[j]]=1;
            if(i%prime[j]==0)
                break;
        }
    }
    for(i=1;i<=m;i++){
        scanf("%d",&v);
        if(a[v]==0)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;

}

//P3383 【模板】线性筛素数
//https://www.luogu.org/problemnew/show/P3383 在线测评网站
//2018-2-15 9:57 AC
#include <stdio.h>
#include <string.h>
int v[10000100],prime[10000100/2];
void getPrime(int n){//筛法模板 线性筛法 源自《算法竞赛进阶指南》 时间效率比之前Eratoshenes算法要提高一倍
    int i,j;
    memset(v,0,sizeof(v)),prime[0]=0;
    for(i=2;i<=n;i++){
        if(v[i]==0)v[i]=i,prime[++prime[0]]=i;
        for(j=1;j<=prime[0];j++){
            if(prime[j]>v[i]||prime[j]*i>n)break;
            v[i*prime[j]]=prime[j];
        }
    }
}
int main(){
    int n,m,a;
    scanf("%d%d",&n,&m);
    getPrime(n);
    while(m--){
        scanf("%d",&a);
        if(v[a]==a)printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

2017-6-14 15:35 AC该单元

猜你喜欢

转载自blog.csdn.net/mrcrack/article/details/61625530