例 - 九公の再配置
トピックの背景
たとえば、下の最初の写真の九公グリッドには、1 から 8 までの数字カードがあり、もう 1 つの空のグリッドがあります。空きスペースに隣接するグリッド内のカードは、空きスペースに移動できます。数回動かすと、2 番目の図に示す状況が形成されます。
最初の写真の状況を 12345678 として記録します。
2 番目の写真の状況を次のように記録します: 123.46758
どうやら数字は上から下、左から右に記録され、スペースはピリオドとして記録されます。
このトピックのタスクは、九功の初期状態と最終状態を知り、少なくとも何段階まで到達できるかを調べることです。何歩進んでも到達できない場合は-1を出力します。
入力フォーマット
入力の最初の行には九功の初期状態が含まれ、二行目には九功の最終状態が含まれています。
出力フォーマット
最小ステップ数を出力するか、解決策が存在しない場合は -1 を出力します。
入力サンプルと出力サンプル
入力
12345678.123.46758 _
出力
3
参照コード
#include<iostream>
#include<queue>
#include<map>
using namespace std;
int dx[4]={1,-1,3,-3};
map<string,int> mp;
string s,e;
struct node
{
string str;
int step;
};
bool no(int x,int y)
{
if(x==2&&y==3) return 1;
if(x==3&&y==2) return 1;
if(x==5&&y==6) return 1;
if(x==6&&y==5) return 1;
return 0;
}
void bfs()
{
queue<node> q;
node tmp,now;
tmp.step=0;
tmp.str=s;
q.push(tmp);
while(q.size())
{
now=q.front();q.pop();
if(now.str==e)
{
cout<<now.step<<endl;
return;
}
int x=now.str.find('.');
for(int i=0;i<4;i++)
{
s=now.str;
int y=x+dx[i];
if(y<0||y>8) continue;
if(no(x,y)) continue;
swap(s[x],s[y]);
if(mp[s]) continue;
mp[s]=1;
tmp.step=now.step+1;
tmp.str=s;
q.push(tmp);
}
}
cout<<"-1"<<endl;
}
int main()
{
cin>>s>>e;
mp[s]=1;
bfs();
return 0;
}
ある例から別の例への推論 - ラッキーナンバー
トピックの背景
ラッキーナンバーはポーランドの数学者ウラムにちなんで名付けられました。素数の生成と同様の「ふるい法」を用いて生成されます。
まず自然数1,2,3,4,5,6,.... を 1 から書きます。
1は最初の幸運な数字です。
番号 2 から始めます。シリアル番号が 2 で割り切れる項目をすべて削除し、1 _ 3 _ 5 _ 7 _ 9 …に変更します。
それらを絞り込んで、1 3 5 7 9 ...のように並べ替えます。
このとき、2番目のラッキーナンバーは「3」なので、連番の3で割り切れる数字をすべて削除します。シリアル番号自体が 3 で割り切れるかどうかではなく、シリアル番号の位置であることに注意してください!! 削除されたものは5、11、17、...である必要があります。
このとき、7が3番目のラッキーナンバーなので、7で割り切れる連番位置(19,39,...)を削除します。
最後に残ったシーケンスは次のようなものです: 1、3、7、9、13、15、21、25、31、33、37、43、49、51、63、67、69、73、75、79、... 。
入力フォーマット
2 つの正の整数 mn をスペースで区切って入力します (m < n < 1000*1000)
出力フォーマット
プログラムは、m から n までのラッキーナンバーの数を出力します (m と n を除く)。
入力サンプルと出力サンプル
入力サンプル
1 20
出力サンプル
5
参照コード
# include<stdio.h>
# define MAX 50000
int create_luck(int * );
int num_luck(int *,int,int,int);
int main(void)
{
int luck[MAX] = {0};
int max,n,i;
int num1,num2;/*标定范围*/
max = create_luck(luck);/*幸运数生成*/
scanf("%d%d",&num1,&num2);
n = num_luck(luck,max,num1,num2);/*计算num1到num2之间的幸运数个数*/
printf("%d\n",n);
/*for(i=0;i<max;i++){
printf("%d\t",luck[i]);//遍历幸运数
}*/
return 0;
}
int create_luck(int * luck)
{
/*1~2*MAX的幸运数*/
int i,j;
int point_pre=1;/*point_pre用来标记被除数数组下标,也就是上一次循环找到的幸运数*/
int point_max=MAX;/*point_max用来标记遍历一遍完成删除之后剩余的元素个数*/
/*原始奇数数组,第一次将偶数完全删除,所以所有幸运数在奇数中生成即可*/
for(i=0;i<MAX;i++){
luck[i] = 2*i+1;
}
/*生成幸运数*/
while(point_pre < point_max){
for(j = point_pre, i = point_pre; j<point_max; j++){/*i,j从point_pre开始,因为point_pre之前的数都已经是幸运数了*/
if((j+1) % luck[point_pre] != 0){
luck[i] = luck[j];
i++;
}
}
point_pre++;/*幸运数个数加1,也是内层循环初始值后移1*/
point_max = i;/*便遍历一遍之后,i之前的符合局部条件(取余不为0),point_max则划分界限*/
}
/*注意:循环完成时,point_pre之前的数都是幸运数,
但是数组定义长为MAX,point_max之后的数(MAX-point_pre个)是无用的
*/
return point_max;
}
int num_luck(int * luck,int max,int num1,int num2)
{
int i,n=0;
for(i=0;i < max;i++){/*i<point_max是为了确保不会找到无用的数组后半部分*/
if(luck[i] >= num2)
break;
else if(luck[i] > num1)/*不包含num1和num2*/
n++;
}
return n;
}