您的位置:首页 > 其它

USCAO section 2,2 Preface Numbering(算是DP吧)

2012-08-29 16:47 281 查看
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


思路:因为这些数有规律,以10为单位会循环,如9,19,29,都会出现相同的9,因此只要加上就行。

I II III IV V VI VII VIII IX X

1 2 3 4 5 6 7 8 9 10

可以发现以I的10个循环位内是{1,2,3,1,0,1,2,3,1,0}

而5的权位V的10 个循环为内是{0 0 0 1 1 1 1 1 0 0}

而10有可以看成特殊的1 ,即权值为10的1;

因此只要处理9就行了,当有9出现时需要在其大2个权X上加1

题目要求N以内的和,使用DP加上即可。

具体看代码吧!

/*
ID:nealgav1
PROG:preface
LANG:C++
*/
#include<fstream>
#include<cstring>
using namespace std;
const int mm=3600;
const int one[]={1,2,3,1,0,1,2,3,1,0};
const int five[]={0,0,0,1,1,1,1,1,0,0};
const char flee[]={'I','V','X','L','C','D','M'};
int f[mm][10];
void dp(int m)
{ memset(f,0,sizeof(f));
for(int i=1;i<=m;i++)
{ int num=i;
for(int j=0;j<7;j++)
{ if(num<=0)   //当num=0时需把原先后面的值加上
{f[i][j]+=f[i-1][j];
continue;
}
if(num%10)
{
if(j&1)
f[i][j]+=f[i-1][j]+five[num%10-1];
else {f[i][j]+=f[i-1][j]+one[num%10-1];
if(num%10==9)//当num=9时有一个高位出现需加上1
f[i][j+2]++;
}

}
else
{
f[i][j]+=f[i-1][j]+one[num%10-1];
}
if(j&1)
num/=10;
}
}
}
int main()
{
int m;
ifstream cin("preface.in");
ofstream cout("preface.out");
cin>>m;
dp(m);
for(int i=0;i<7;i++)
{
if(f[m][i])
cout<<flee[i]<<" "<<f[m][i]<<"\n";
}
}


Preface Numbering

Russ Cox
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);
}


Cary Yang sends in this concise solution that works more the way people think about roman numerals, without the 'IV trick':

#include <fstream>
using namespace std;
int count[7];
int mult[6] = {5, 2, 5, 2, 5, 2}; // The factors between consecutive roman
// numeral letter values.
char roman[] = "IVXLCDM";
int vals[7] = {1, 5, 10, 50, 100, 500, 1000};

int main() {
ofstream fout ("preface.out");
ifstream fin ("preface.in");
int n;
fin >> n;

for (int i = 1; i <= n; i++) {
for (int j = 0, temp = i; temp != 0; j++) {
// If there are more than three of the current letter.
if (temp % mult[j] > 3) {
count[j]++;
// Checks if it can have a two letter difference
// (ie. IX instead of IV).
if (temp / mult[j] > 0 && i % vals[j + 2] > vals[j + 1]) {
count[j + 2]++;
temp -= mult[j];
} else
count[j + 1]++;
} else
count[j] += temp % mult[j];
temp /= mult[j];
}
}
for (int i = 0; i < 7; i++)
if (count[i])
fout << roman[i] << " " << count[i] << endl;
return 0;
}


USER: Neal Gavin Gavin [nealgav1]
TASK: preface
LANG: C++

Compiling...
Compile: OK

Executing...
Test 1: TEST OK [0.011 secs, 3480 KB]
Test 2: TEST OK [0.011 secs, 3480 KB]
Test 3: TEST OK [0.000 secs, 3480 KB]
Test 4: TEST OK [0.000 secs, 3480 KB]
Test 5: TEST OK [0.000 secs, 3480 KB]
Test 6: TEST OK [0.011 secs, 3480 KB]
Test 7: TEST OK [0.000 secs, 3480 KB]
Test 8: TEST OK [0.000 secs, 3480 KB]

All tests OK.
Your program ('preface') produced all correct answers!  This is your
submission #3 for this problem.  Congratulations!

Here are the test data inputs:

------- test 1 ----
1
------- test 2 ----
20
------- test 3 ----
100
------- test 4 ----
500
------- test 5 ----
1000
------- test 6 ----
2974
------- test 7 ----
3213
------- test 8 ----
3499

Keep up the good work!


Thanks for your submission!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: