USACO Preface Numbering

1、可以发现带有四、九的数字都是比较特殊的。。。抽象出它们的共性,写成一个函数,每次只改变几个参数,然后暴力枚举即可。

/*
ID:mrxy564
PROG:preface
LANG:C++
*/
#include<cstdio>
#include<cstring>
using namespace std;
int n,a[4],b[9];
char c[7]={'I','V','X','L','C','D','M'};
void count(int bit,int x,int y,int z){
   switch(a[bit]){
     case 1:
   b[x]++;
   break;
  case 2:
   b[x]+=2;
   break;
  case 3:
   b[x]+=3;
   break;
  case 4:
   b[x]++;b[y]++;
   break;
  case 5:
   b[y]++;
   break;
  case 6:
   b[x]++;b[y]++;
   break;
  case 7:
   b[x]+=2;b[y]++;
   break;
  case 8:
   b[x]+=3;b[y]++;
   break;
        case 9:
   b[x]++;b[z]++;
   break;
 }
}
int main(){
 freopen("preface.in","r",stdin);
 freopen("preface.out","w",stdout);
 scanf("%d",&n);
 memset(b,0,sizeof(b));
 for(int i=1;i<=n;i++){
   int temp=4,num=i;
   while(temp){
         a[temp-1]=num%10;
      num/=10;
   temp--;
   }
   count(3,0,1,2);
   count(2,2,3,4);
   count(1,4,5,6);
   count(0,6,7,8);
 }
 for(int i=0;i<7;i++)
   if(b[i])
     printf("%c %d\n",c[i],b[i]);
 return 0;
}

官方题解:

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值