USACO:2.2.1 Preface Numbering 序言页码

USACO:2.2.1 Preface Numbering 序言页码

一、题目描述

★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

有时,一个可表示为10^n 的数出现在一个比它大的数前(I 在V 或X 前面,X 在L 或C 前面,等等).

在这种情况下,数值等于后面的那个数减去前面的那个数:

IV = 4

IX = 9

XL = 40

像XD, IC, 和XM 这样的表达是非法的,因为前面的数比后面的数小太多.对于XD(490 的错误表达),

可以写成 CDXC; 对于IC(99 的错误表达),可以写成XCIX; 对于XM(990 的错误表达),可以写成

CMXC.

给定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

一个整数N.

SAMPLE INPUT(preface.in)

5

OUTPUT FORMAT

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

SAMPLE OUTPUT(preface.out)

I 7

V 2

二、解题思路

首先我们都能想到的就是枚举,枚举一般来说比较简单,思路清晰。没有什么复杂的算法和数据结构。

我们仔细分析题目,发现如下规律:(分析找规律的建模过程很重要!)

标准数字表:

I   1     L   50   M  1000

V   5     C  100

X  10     D  500 

可建一个表存储,ch[8]={‘I‘,‘V‘,‘X‘,‘L‘,‘C‘,‘D‘,‘M‘}

分析得出,对应个、十、百和千位的数字表

(‘‘,‘I‘,‘II‘,‘III‘,‘IV‘,‘V‘,‘VI‘,‘VII‘,‘VIII‘,‘IX‘),//个位,0,1,2,...,9

(‘‘,‘X‘,‘XX‘,‘XXX‘,‘XL‘,‘L‘,‘LX‘,‘LXX‘,‘LXXX‘,‘XC‘),//十

(‘‘,‘C‘,‘CC‘,‘CCC‘,‘CD‘,‘D‘,‘DC‘,‘DCC‘,‘DCCC‘,‘CM‘),//百

(‘‘,‘M‘,‘MM‘,‘MMM‘,‘‘,‘‘,‘‘,‘‘,‘‘,‘‘));//千

通过查看上表,排列相当有规律。从个位到千位,不仅形式没变,而且字符变化是标准数字表中字符位置的相同偏移递增。因此,我们可以利用这个特征来简化枚举的操作,对每个数字从个位到千位,通过字符偏移量进行查表,逐位进行字符计数。

我的程序实现如下:

#include <iostream>
#include <cstdio>

using namespace  std;

int N;
int vis[7];
char c[8]={'I','V','X','L','C','D','M'};
// I   1     L   50   M  1000
// V   5     C  100
// X  10     D  500
// ('','I','II','III','IV','V','VI','VII','VIII','IX'),//个
// ('','X','XX','XXX','XL','L','LX','LXX','LXXX','XC'),//十
// ('','C','CC','CCC','CD','D','DC','DCC','DCCC','CM'),//百
// ('','M','MM','MMM','','','','','',''));//千
void cnt(int x,int digit){
	//个位
	//if(digit==1){
	int var;
	var=2*(digit-1);//对应个、十、百和千数位的数字表相对位移
	if (1<=x&&x<=3){
		vis[0+var]=vis[0+var]+x;}
	else if(x==4){
		vis[0+var]=vis[0+var]+1;
		vis[1+var]=vis[1+var]+1;
	}
	else if(x==5){
		vis[1+var]=vis[1+var]+1;
	}
	else if (6<=x&&x<=8){
		vis[0+var]=vis[0+var]+x-5;
		vis[1+var]=vis[1+var]+1;
	}
	else if (x==9){
		vis[0+var]=vis[0+var]+1;
		vis[2+var]=vis[2+var]+1;
	}
}

void dfs(int N,int digit){
    int num=N%10;
	if (num!=0){ //&& (num%10)!=0
		cnt (num,digit);}

	if(N/10!=0){
         N=N/10;
		dfs(N,digit+1);
	}
}

int main(){
	freopen("preface.in","r",stdin);
	freopen("preface.out","w",stdout)p;
	int i;
    cin>>N;
    for(i=0;i<=N;i++)
        dfs(i,1);

	for (i=0;i<7;i++)
	{	if (vis[i]!=0)
	cout<<c[i]<<" "<<vis[i]<<endl;
	}

	return 0;
}

//USACO参考答案
// 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"));//拼接1000
	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; //将字符的ascii值为索引的位置初始化为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);
}

由于自身是初学者,编程能力有限,未达到专业程序员的水平,可能误导大家,请大家甄读;文字编辑也一般,文中可能会有措辞不当。博文中的错误和不足敬请读者批评指正。

时间: 2024-11-04 19:17:12

USACO:2.2.1 Preface Numbering 序言页码的相关文章

2.2.1 PREFACE NUMBERING 序言页码

http://acm.sdibt.edu.cn/JudgeOnline/problem.php?id=2325 题目大意:(如题) 输入输出:(如题) 解题思路: 1.用打表法将每一个数N(1<=N<3500)中间"I""V""X""L""C""D""M"的个数统计出来,用一个二维数组cnt[3500][7]保存起来. 2.枚举. 从千位開始枚举.一直枚举

USACO Section 2.2 Preface Numbering

/* ID: lucien23 PROG: preface LANG: C++ */ #include <iostream> #include <fstream> #include <string> #include <map> using namespace std; int main() { ifstream infile("preface.in"); ofstream outfile("preface.out")

【USACO 2.2】Preface Numbering (找规律)

求 1-n 的所有罗马数字表达中,出现过的每个字母的个数. 分别对每个数的罗马表达式计算每个字母个数. 对于十进制的每一位,都是一样的规则,只是代表的字母不同. 于是我们从最后一位往前考虑,当前位由字母 s[i] 代表 1,字母 s[i+1] 代表 5,s[i+2] 代表 10(在下一次代表1). 每一位考虑完 i+=2; num[i] 为当前位为i对应的 s[i] 的个数,当前位为 4~8 时,s[i+1] 出现 1 次,当前位为 9 时,s[i+2] 出现一次. http://train.u

USACO Section 2.1: Preface Numbering

看网上的,太琐碎 1 /* 2 ID: yingzho2 3 PROG: preface 4 LANG: C++ 5 */ 6 #include <iostream> 7 #include <fstream> 8 #include <string> 9 #include <map> 10 #include <vector> 11 #include <set> 12 #include <algorithm> 13 #incl

【USACO 2.2.1】序言页码

[题目描述] 一类书的序言是以罗马数字标页码的.传统罗马数字用单个字母表示特定的数值,以下是标准数字表: 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 有时,一个可表示为

USACO 2.2 Preface Numbering

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 5

洛谷 P1465 [USACO2.2]序言页码 Preface Numbering

题目描述 一类书的序言是以罗马数字标页码的.传统罗马数字用单个字母表示特定的数值,以下是标准数字表: I 1 V 5 X 10 L 50 C 100 D 500 M 1000 最多3个同样的可以表示为10n的数字(I,X,C,M)可以连续放在一起,表示它们的和: III=3 CCC=300 可表示为5x10n的字符(V,L,D)从不连续出现. 除了下一个规则,一般来说,字符以递减的顺序接连出现: CCLXVIII = 100+100+50+10+5+1+1+1 = 268 有时,一个可表示为10

USACO Preface Numbering 构造

一开始看到这道题目的时候,感觉好难 还要算出罗马的规则. 但是仔细一看,数据规模很小, n 只给到3500 看完题目给出了几组样例之后就有感觉了 解题方法就是: n的每个十进制数 转换成相应的罗马数字,然后统计每个罗马数字出现的次数即可 还是一道简单的构造题. (以下摘自https://www.byvoid.com/blog/usaco-221preface-numbering/) 转化有以下规则: 1.数较大部分在前,较小部分在后 2.表示10整倍数的字母(I X C M)最多可以累加三次 3

usaco Preface Numbering

题目算法不难,难的是读懂题意,意思是从1到N的数字转换成罗马数字,然后统计所有数字中的各种字母出现的次数 对于每个数,用贪心的方法转换为罗马数字,然后统计就好了 /* ID: modengd1 PROG: preface LANG: C++ */ #include <iostream> #include <stdio.h> #include <memory.h> #include <string> #include <cstring> using