前言:题目来源深职院计科老班。
题目:有2n个1到9的整数为:2个“1”,2个“2”,….,2个”n”。看谁能用这2n个数字拼成一个数,满足2个“1”中间有1个数字,2个“2”中间有2个数字,…,2个“n”中间有n个数字。
输入:一个整数"n",(1<=n<=9)
输出:由小到大顺序输出满足条件拼出的数字,每个数字占据一行
题目中升序输出给我一个从小到大遍历的思路。第一个版本用的暴力循环,毫无意外的超时。
在认为这条路走不通,开始考虑dp时,发现每一对数字的位置相对来说都是固定的。
用数字2举例,当第一个2在数字的第1位时,那么第二个2必然在第1+2+1这个位置上。
2XX2XX
那么我们枚举每一个结果时,通过一个bool数组来记录每一个位置的使用情况,当需要用到的位置被占用了,就直接pass掉。虽说形式和第一个版本的暴力循环大致相同,但是效率上大大提高了。
思路清晰了,直接通过循环+递归的方式找寻答案。
#include<stdio.h>
#include<stdbool.h>
#include<string.h>
int max, arr[30];
bool flag[30], num[30];
void set(int p){
int a, b;
// 满足条件直接输出
if(p==max){
for(a=1; a<=max*2; a++) printf("%d", arr[a]);
printf("\n");
return;
}
// 找距离队首最近的空位
a=p;
while(flag[a]) a++;
// 开始遍历未使用的数字
for(b=1; b<=max; b++){
if(num[b] || flag[a+b+1]) continue;
if(a+b+1>max*2) return;
flag[a]=flag[a+b+1]=num[b]=true;
arr[a]=arr[a+b+1]=b;
set(p+1);
flag[a]=flag[a+b+1]=num[b]=false;
}
return;
}
int main(void){
scanf("%d", &max);
int a;
for(a=1; a<=max; a++){
memset(flag, false, sizeof(flag));
memset(num, false, sizeof(num));
// 遍历队首放入数字
flag[1]=flag[a+2]=true;
arr[1]=arr[a+2]=a;
num[a]=true;
set(1);
}
return 0;
}