首先我们都能想到的就是枚举,枚举一般来说比较简单,思路清晰。没有什么复杂的算法和数据结构。
我们仔细分析题目,发现如下规律:(分析找规律的建模过程很重要!)
标准数字表:
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);
}
USACO:2.2.1 Preface Numbering 序言页码
原文地址:http://blog.csdn.net/finded/article/details/46289185