USACO:2.2.3 Runaround Numbers 循环数
题目描述
★Runaround Numbers 循环数
循环数是那些不包括0 这个数字的没有重复数字的整数 (比如说, 81362) 并且同时具有一个有趣的性质, 就像这个例子:如果你从最左边的数字开始 ( 在这个例子中是8) 数最左边这个数字个数字到右边(回到最左边如果数到了最右边).你会停止在另一个新的数字(如果没有停在一个不同的数字上,这个数就不是循环数). 就像: 8 1 3 6 2 从最左边接下去数8 个数字: 1 3 6 2 8 1 3 6 所以下一个数字是6.重复这样做 (这次从“6”开始数6 个数字) 并且你会停止在一个新的数字上: 2 8 1 3 6 2, 也就是2.
再这样做 (这次数两个): 8 1
再一次 (这次一个): 3
又一次: 6 2 8 这是你回到了起点, 在从每一个数字开始数1 次之后. 如果你在从每一个数字开始数一次以后没有回到起点, 你的数字不是一个循环数.
给你一个数字 M (在1 到9 位之间), 找出第一个比 M 大的循环数, 并且一定能用一个无符号长整形数装下.
PROGRAM NAME: runround
INPUT FORMAT
仅仅一行, 包括M
SAMPLE INPUT (file runround.in)
81361
OUTPUT FORMAT
仅仅一行,包括第一个比M 大的循环数.
SAMPLE OUTPUT (file runround.out)
81362
解题思路
首先我们一定要仔细阅读题目,理解题意。从开始数往后进行枚举,循环数是那些不包括0 这个数字的没有重复数字的整数,剔除不满足条件的,然后判断其是不是runround number,如果是就输出退出。还有一些细节处理。下面是源代码:
//我的解
#include <iostream>
#include<string.h>
#include<cstdio>
using namespace std;
int M;
int vis[9+1];
int flag=1;
int rst;
void isrunaround(int x){
char str1[10];
int vv[10],i,j,len,next_k,k=0,cnt=0;;
memset(vv,0,10*4);
sprintf(str1,"%d",x);//数字输出到字符串sscanf用法
len=strlen(str1);
for (i=0;i<len;i++)
{ vv[str1[i]-48]=vv[str1[i]-48]+1;//0-9每位数个数计数,单字符与数字相差的是48
if (vv[0]==1||vv[str1[i]-48]==2) return;//循环数不包括0,没有重复数字的整数
}
for (;;)
{ if(cnt==len){//是循环数,遍历了每个数字
flag=0;//成功找到
rst =x;//记录结果
return;
}
j=str1[k]-48;
vis[k]=1;//访问过
next_k=(k+j)%len;//下一个数的位置
if (vis[next_k]==1&&next_k!=0) return;//访问过且不是回到了起点
k=next_k;
cnt++;
}
}
int main()
{
freopen("runround.in","r",stdin);
//freopen("runround.out","w",stdout);
cin>>M;
int i;
for(i=M+1;flag;i++){
memset(vis,0,10*4);
vis[i]=1;
isrunaround(i);
}
cout<<rst<<endl;
return 0;
}
//官方原解
//The trick to this problem is noticing that since runaround numbers must have unique digits, they must be at most 9 digits long. There are only 9! = 3628//80 nine-digit numbers with unique digits, so there are fewer than 9*362880 numbers with up to 9 unique digits. Further, they are easy to generate, so we//generate all of them in increasing order, test to see if they are runaround, and then take the first one bigger than our input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int m;
FILE *fout;
/* check if s is a runaround number; mark where we've been by writing 'X' */
int
isrunaround(char *s)
{
int oi, i, j, len;
char t[10];
strcpy(t, s);
len = strlen(t);
i=0;
while(t[i] != 'X') {
oi = i;
i = (i + t[i]-'0') % len;//计算下一个数,进行遍历操作
t[oi] = 'X';
}
/* if the string is all X's and we ended at 0, it's a runaround */
if(i != 0)//没有回到原点
return 0;
for(j=0; j<len; j++)
if(t[j] != 'X')//没有遍历所有的数位
return 0;
return 1;
}
/*
* create an md-digit number in the string s.
* the used array keeps track of which digits are already taken.
* s already has nd digits.
*/
void
permutation(char *s, int *used, int nd, int md)//1-9产生md位数的排列,
{
int i;
if(nd == md) {
s[nd] = '\0';
if(atoi(s) > m && isrunaround(s)) {
fprintf(fout, "%s\n", s);
exit(0);
}
return;
}
for(i=1; i<=9; i++) {
if(!used[i]) {
s[nd] = i+'0';//数字转化为字符
used[i] = 1; //递归式枚举,保证不重复。
permutation(s, used, nd+1, md);
used[i] = 0;
}
}
}
void
main(void)
{
FILE *fin;
char s[10];
int i, used[10];
fin = fopen("runround.in", "r");
fout = fopen("runround.out", "w");
assert(fin != NULL && fout != NULL);
fscanf(fin, "%d", &m);
for(i=0; i<10; i++)
used[i] = 0;
for(i=1; i<=9; i++)
permutation(s, used, 0, i);
assert(0); /* not reached */
}
由于自身是初学者,编程能力有限,未达到专业程序员的水平,可能误导大家,请大家甄读;文字编辑也一般,文中可能会有措辞不当。博文中的错误和不足敬请读者批评指正。
部分引自 http://pingce.ayyz.cn:9000/usaco/20110129214306/subset_001.html