USACO-Section 2.2 Preface Numbering (数学)

描述

一类书的序言是以罗马数字标页码的。传统罗马数字用单个字母表示特定的数值,以下是标准数字表:

I 1  L 50  M 1000
V 5  C 100
X 10 D 500

最多3个同样的可以表示为10n的数字(I,X,C,M)可以连续放在一起,表示它们的和:

III=3
CCC=300

可表示为5x10n的字符(V,L,D)从不连续出现。

除了下一个规则,一般来说,字符以递减的顺序接连出现:

CCLXVIII = 100+100+50+10+5+1+1+1 = 268

有时,一个可表示为10n的数出现在一个比它大1级或2级的数前(I在V或X前面,X在L或C前面,等等)。在这种情况下,数值等于后面的那个数减去前面的那个数:

IV = 4
IX = 9
XL = 40

This compound mark forms a unit and may not be combined to make another compound mark (e.g., IXL is wrong for 39; XXXIX is correct).

像XD, IC, 和XM这样的表达是非法的,因为前面的数比后面的数小太多。对于XD(490的错误表达),可以写成 CDXC; 对于IC(99的错误表达),可以写成XCIX; 对于XM(990的错误表达),可以写成CMXC。 90 is expressed XC and not LXL, since L followed by X connotes that successive marks are X or smaller (probably, anyway).


给定N(1 <= N < 3,500), 序言的页码数,请统计在第1页到第N页中,有几个I出现,几个V出现,等等 (从小到大的顺序)。不要输出并没有出现过的字符。

比如N = 5, 那么页码数为: I, II, III, IV, V. 总共有7个I出现,2个V出现。

格式

PROGRAM NAME: preface

INPUT FORMAT:

(preface.in)

一个整数N。

OUTPUT FORMAT:

(preface.out)

每行一个字符和一个数字k,表示这个字符出现了k次。字符必须按数字表中的递增顺序输出。

SAMPLE INPUT

5

SAMPLE OUTPUT

I 7
V 2

这么简单一道题都不能快速做出来,卡了好久,明明有枚举的方法还非要用数学方法。。。

也可能是开始算法对了,但是忘记处理下标越界和为负数的情况,导致结果一直不对。。。


/*
ID: your_id_here 
PROG: preface
LANG: C++
*/
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int nn[3][10]={0,1,2,3,1,0,1,2,3,1,
                     0,0,0,0,1,1,1,1,1,0,
                     0,0,0,0,0,0,0,0,0,1};
const int nm[3][10]={0,1,3,6,7,7,8,10,13,14,
                     0,0,0,0,1,2,3,4,5,5,
                     0,0,0,0,0,0,0,0,0,1};
int n,num[11],t,t1,t2,tt,tp;//num数组开小了,又是本地对,OJ错...
char ch[8]="IVXLCDM";

int main() {
    int i;
    freopen("preface.in","r",stdin);
    freopen("preface.out","w",stdout);
    while(1==scanf("%d",&n)) {
        for(i=0;i<8;++i)
            num[i]=0;
        i=0,tp=1,tt=1,t=n;
        while(t) {
            t1=t/10;
            tt=t%10-1;
            if(tt==-1)//可能存在数组越界的问题,所以tt为-1时将其赋为0
                tt=0;
            //每次计算的时候分为三部分:假设目前数字为abc
            //Ⅰ:当前看个位
            //①ab(即c前面的数字)个0~9完整出现过 ②1个0~c-1完整出现过 ③n%tp+1(即c后面的数字+1=0+1)个c出现过
            //Ⅱ:当前看十位
            //①a(即b前面的数字)个0~9完整出现过 ②1个0~b-1完整出现过 ③n%tp+1(即b后面的数字+1=b+1)个b出现过
            //Ⅲ:当前看百位
            //①0(即a前面的数字)个0~9完整出现过 ②1个0~a-1完整出现过 ③n%tp+1(即b后面的数字+1=a+1)个a出现过
            num[i]+=t1*nm[0][9]*tp+nm[0][tt]*tp+(n%tp+1)*nn[0][t%10];
            num[i+1]+=t1*nm[1][9]*tp+nm[1][tt]*tp+(n%tp+1)*nn[1][t%10];
            num[i+2]+=t1*nm[2][9]*tp+nm[2][tt]*tp+(n%tp+1)*nn[2][t%10];
            t=t1;
            i+=2;
            tp*=10;
        }
        i=0;
        while(num[i]) {
            printf("%c %d\n",ch[i],num[i]);
            ++i;
        }
    }
    return 0;
}


你可能感兴趣的:(USACO)