ACM学习历程28——利用数组下标

数组是在ACM程序设计大赛中经常用到的一类复合数据类型,对于一个数组类型的变量,我们可在变量声明的时候声明变量的类型,例如:整型数组、字符数组等。对于普通字符数组,其实是数组每个单元中存储了一个字符;对于字符串,实际上除了在每个存储位置存储了一个一个字符外,在字符串结束的位置还额外存储了一个’\0’作为字符串借书的标志。在这篇博文中,并不介绍数组在存储上的一些特性,只是想说明在解答题目的过程中,若是涉及到对数组的操作尤其是涉及到跟数组下标相关的一些操作,要学习利用下标之间的关系,这样设计出来的代码比较简洁高效。下面以具体的题目为例进行说明,希望在进行题目解答时,留意相关细节。
一、环装序列求解
题目:长度为N的环装序列有N种表示方法,分别从某个位置开始顺时针得到一个序列。例如一个环装序列为AGCTTTG,那么可以得到的序列有AGCTTTG、GCTTTGA、CTTTGAG、TTTGAGC、TTGAGCT、TGAGCTT、GAGCTTT。现在的任务是要从以上环状序列中按照字典序列找出最小表示。比如在CTCC的所有序列中的最小表示序列是CCCT,CGAGTCAGCT的最小表示是AGCTCGAGTC。注意,题目所说的字典顺序,指的是字符串在字典出现的顺序。对于两个字符串,从第一个字符开始比较,当某一个位置的字符串不同时,该字符较小的串,字典序较小,例如ABC比BCD小;如果其中一个字符已经没有更多字符,但是另外一个字符串还没有结束,那么我们认为字符串较短的串字典序列较小,例如AB比ABC小。
该题目的解答代码:

#include<iostream>
#include<cstring>
using namespace std;

int getIndex(char s[],int p, int start)
{
    int i;
    int len=strlen(s);
    for(i=0;i<len;i++)
    {
        if(s[(i+p)%len]!=s[(i+start)%len])
            return s[(i+p)%len] < s[(i+start)%len];
    }
    return 0;
}

int main()
{
    char s[512];
    int i,len;
    while(cin.getline(s,512))
    {
        len=strlen(s);
        int start=0;
        for(i=1;i<len;i++)
        {
            if(getIndex(s,i,start))
            {
                start=i;
            }
        }
        for(i=0;i<len;i++)
            cout<<s[(i+start)%len];
        cout<<endl;
    }
    return 0;
}

在整个题目中,如果通过BF算法思想,即通过暴力求解的方式,也是可以求解出最终的结果,就是说我们列举出这个环状序列所有可能的子序列,按照字典顺序分别求出每个子序列的逆序数。但是这样做的缺点是需要开辟一个跟原环状序列一样大小的字符串用于存储中间结果,浪费存储空间。在上面的解答代码中,利用了数组小标的关系,我们只需要找出最小环状序列的起始位置即可。若该最小环状序列最小起始位置为start,当然start介于0—N-1之间,然后利用S[(i+n)%n],i自加十次并输出十次即可得到结果。但在是个问题的解法中,使用了start变量来记录最小环状序列可能出现的位置。在每一次函数调用中,传入i和start的位置,在函数体内相对i和start的位置自加,若每次相对自加后,两个字符依然相等则继续,直到不相等为止。在N次函数调用结束后,即可找到最小环状序列起始的位置。
二、利用数组下标的关系可以是一些问题变得更加简单,再举一个问题。问题描述如下:N个人站成一圈,逆时针编号为1—N。有两个官员,A从1开始逆时针数,B开始从N开始顺时针数。在每一轮中,官员A数K个就停下来,官员B数M个就停下来。注意:有可能两个人在停下来的时候指向同一个人。被选中的人在每一轮结束的时候出队,直到所有的人出队。输入N、K、M,输出每轮呗选中人的编号,如N=10,K=4,M=3,输出为:4 8;9 5;3 1;2 6;10;7。
该题目的解答代码:

#include<iostream>
using namespace std;

int n,k,m,a[128];

int findPos(int p,int d,int t)
{
    //数t次
    while(t--)
    {
        do
        {
            p=(p+d+n-1)%n+1;
        }
        while(a[p]==0);
        //如果这个位置为0,说明该位置已经出队,继续寻找下一个
    }
    return p;
}

int main()
{
    while(cin>>n>>k>>m && n)
    {
        //初始化a
        int i;
        for(i=1;i<=n;i++)
            a[i]=i;
        //开始时剩下N个人,一个人从N开始数,一个人从1开始数
        int left=n,p1=n,p2=1;
        while(left)
        {
            p1=findPos(p1,1,k);
            p2=findPos(p2,-1,m);
            cout<<p1;
            left--;
            if(p1!=p2)
            {
                cout<<p2;
                left--;
            }
            a[p1]=0;
            a[p2]=0;
            if(left)
                cout<<',';
        }
        cout<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值