usaco——preface

本文介绍了一种计算罗马数字中各字母出现次数的算法。针对一本书前言页码的罗马数字表示,通过不同方法统计I、V、X等字母的使用频次。包括单位数、十位数、百位数及千位数的处理技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Preface Numbering

A certain book's prefaces are numbered in upper case Roman numerals. Traditional Roman numeral values use a single letter to represent a certain subset of decimal numbers. Here is the standard set:

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

As many as three of the same marks that represent 10n may be placed consecutively to form other numbers:

  • III is 3
  • CCC is 300

Marks that have the value 5x10n are never used consecutively.

Generally (with the exception of the next rule), marks are connected together and written in descending order to form even more numbers:

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

    Sometimes, a mark that represents 10^n is placed before a mark of one of the two next higher values (I before V or X; X before L or C; etc.). In this case, the value of the smaller mark is SUBTRACTED from the mark it precedes:

    • 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).

    Compound marks like XD, IC, and XM are not legal, since the smaller mark is too much smaller than the larger one. For XD (wrong for 490), one would use CDXC; for IC (wrong for 99), one would use XCIX; for XM (wrong for 990), one would use CMXC. 90 is expressed XC and not LXL, since L followed by X connotes that successive marks are X or smaller (probably, anyway).

    Given N (1 <= N < 3,500), the number of pages in the preface of a book, calculate and print the number of I's, V's, etc. (in order from lowest to highest) required to typeset all the page numbers (in Roman numerals) from 1 through N. Do not print letters that do not appear in the page numbers specified.

    If N = 5, then the page numbers are: I, II, III, IV, V. The total number of I's is 7 and the total number of V's is 2.

    PROGRAM NAME: preface

    INPUT FORMAT

    A single line containing the integer N.

    SAMPLE INPUT (file preface.in)

    5
    

    OUTPUT FORMAT

    The output lines specify, in ascending order of Roman numeral letters, the letter, a single space, and the number of times that letter appears on preface page numbers. Stop printing letter totals after printing the highest value letter used to form preface numbers in the specified set.

    SAMPLE OUTPUT (file preface.out)

    I 7
    V 2
     
  • 这代码写的,究竟是怎么个问题,我想了想,是语言的一些东西用的不熟吧。所以就没用。看看别人的:
     
    再把官方的黏上:

    Since the maximum problem size is fairly small, it makes sense to just calculate the corresponding roman number for each page number, and count letters.

    The tricky part is generating the roman numbers. The key insight is that roman numbers are not much different than our own decimal digits. The two differences are that the set of digits changes depending on which decimal place we're worrying about, and that sometimes a "digit" is multiple letters or no letters (in the case of zero). So for example, in the ones place 7 is written "VII" and in the tens place "LXX", and so on, but it's always the same format: the letter for 5 and then two occurrences of the letter for 1.

    We use a lookup table called "encode" to encode each digit, translating from the letters for the ones place to the letters for the place that we care about. The "romandigit" function takes care of each digit, and the "roman" function strings them all together.

    /*
    PROG: preface
    ID: rsc001
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    
    static char *encode[] = {
    	"", "I", "II", "III", "IV",
    	"V", "VI", "VII", "VIII", "IX",
    };
    
    char*
    romandigit(int d, char *ivx)
    {
    	char *s, *p;
    	static char str[10];
    
    	for(s=encode[d%10], p=str; *s; s++, p++) {
    		switch(*s){
    		case 'I':
    			*p = ivx[0];
    			break;
    		case 'V':
    			*p = ivx[1];
    			break;
    		case 'X':
    			*p = ivx[2];
    			break;
    		}
    	}
    	*p = '/0';
    	return str;
    }
    
    char*
    roman(int n)
    {
    	static char buf[20];
    
    	strcpy(buf, "");
    	strcat(buf, romandigit(n/1000, "M"));
    	strcat(buf, romandigit(n/100,  "CDM"));
    	strcat(buf, romandigit(n/10,   "XLC"));
    	strcat(buf, romandigit(n,      "IVX"));
    	return buf;
    }
    
    void
    main(void)
    {
    	FILE *fin, *fout;
    	int i, n;
    	char *s;
    	int count[256];
    
    	fin = fopen("preface.in", "r");
    	fout = fopen("preface.out", "w");
    	assert(fin != NULL && fout != NULL);
    
    	fscanf(fin, "%d", &n);
    
    	for(s="IVXLCDM"; *s; s++)
    		count[*s] = 0;
    
    	for(i=1; i<=n; i++)
    		for(s=roman(i); *s; s++)
    			count[*s]++;
    
    	for(s="IVXLCDM"; *s; s++)
    		if(count[*s])
    			fprintf(fout, "%c %d/n", *s, count[*s]);
    
    	exit(0);
    }
    

    Alex Schendner's Algorithm

    Alex writes:
    While you certainly can find out what the Roman numerals are, the problem does not ask for that information and the program can be made simpler if you only keep track of how many for each digit there are. [Kolstad simplified the program slightly.]

    #include <fstream.h>
    
    int     Ig = 0;
    int     Vg = 0;
    int     Xg = 0;
    int     Lg = 0;
    int     Cg = 0;
    int     Dg = 0;
    int     Mg = 0;
    
    inline void 
    roman (int x)
    {
        int     I = 0;
        int     V = 0;
        int     X = 0;
        int     L = 0;
        int     C = 0;
        int     D = 0;
        int     M = 0;
        for ( ; x >= 1000; ++M, x -= 1000);
        for ( ; x >= 500; ++D, x -= 500);
        for ( ; x >= 100; ++C, x -= 100);
        for ( ; x >= 50; ++L, x -= 50);
        for ( ; x >= 10; ++X, x -= 10);
        for ( ; x >= 5; ++V, x -= 5);
        for ( ; x >= 1; ++I, x -= 1);
    
        while (D > 0 && (C / 4) > 0) {
    	--D; C -= 4; ++M; ++C;
        }
        while (C >= 4) {
    	C -= 4; ++D; ++C;
        }
        while (L > 0 && (X / 4) > 0) {
    	--L; X -= 4; ++C; ++X;
        }
        while (X >= 4) {
    	X -= 4; ++L; ++X;
        }
        while (V > 0 && (I / 4) > 0) {
    	--V; I -= 4; ++X; ++I;
        }
        while (I >= 4) {
    	I -= 4; ++V; ++I;
        }
        Ig += I;
        Vg += V;
        Xg += X;
        Lg += L;
        Cg += C;
        Dg += D;
        Mg += M;
        return;
    }
    
    int 
    main ()
    {
    
        int     n;
        ifstream filein ("preface.in");
        filein >> n;
        filein.close ();
    
        for (int loop = 1; loop <= n; ++loop) {
    	roman (loop);
        }
    
        ofstream fileout ("preface.out");
        if (Ig != 0) {
    	fileout << 'I' << ' ' << Ig << endl;
        }
        if (Vg != 0) {
    	fileout << 'V' << ' ' << Vg << endl;
        }
        if (Xg != 0) {
    	fileout << 'X' << ' ' << Xg << endl;
        }
        if (Lg != 0) {
    	fileout << 'L' << ' ' << Lg << endl;
        }
        if (Cg != 0) {
    	fileout << 'C' << ' ' << Cg << endl;
        }
        if (Dg != 0) {
    	fileout << 'D' << ' ' << Dg << endl;
        }
        if (Mg != 0) {
    	fileout << 'M' << ' ' << Mg << endl;
        }
        fileout.close ();
    
        return (0);
    }
    

    lifuxin's Algorithm

    Similar as Alex's program, this one is simpler in some ways. We can treat those things like "IX" or "IV" as another positive number. We will just use the number to try every number in the ns array. I also used match1 and match2 to save the corresponding letter of each number in ns.

    #include <stdio.h>
    int     ns[] =
       {1000, 900, 500, 400, 100, 90, 50,  40, 10,  9,  5,   4, 1};
       //"M" "CM"  "D"  "CD" "C" "XC" "L" "XL" "X" "IX" "V" "IV" "I"
    char    rs[] = {"IVXLCDM"};
    int     match1[] = {6, 4, 5, 4, 4, 2, 3, 2, 2, 0, 1, 0, 0};
    int     match2[] = {-1, 6, -1, 5, -1, 4, -1, 3, -1, 2, -1, 1, -1};
    int     n;
    int     counts[7];
    
    void 
    count (int num)
    {
        int     sct[] = {3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3};
        int     i, j = 0;
        for (i = 0; i < 13; i++) {
    	while (sct[i] > 0) {
    	    if ((num - ns[i]) >= 0) {
    		num -= ns[i];
    		counts[match1[i]]++;
    		if (match2[i] >= 0)
    		    counts[match2[i]]++;
    		sct[i]--;
    	    }
    	    else
    		break;
    	}
        }
    }
    
    void 
    main ()
    {
        FILE   *fp = fopen ("preface.in", "r");
        FILE   *wfp = fopen ("preface.out", "w");
        int     i;
        fscanf (fp, "%d", &n);
        for (i = 0; i < 7; i++)
    	counts[i] = 0;
        for (i = 1; i <= n; i++)
    	count (i);
        for (i = 0; i < 7; i++) {
    	if (counts[i])
    	    fprintf (wfp, "%c %d/n", rs[i], counts[i]);
        }
        fclose (fp);
        fclose (wfp);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值