USACO:2.2.3 Runaround Numbers 循环数

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝亦

感谢博主辛勤的付出

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值