【题目】就是有标号1到6的六只瓶子。和1到6的盖子。把瓶盖安在瓶子上 求瓶子和瓶盖数字没有任何相同的情况
善良的黑藻同学找到这个问题 我发现自己高中的数学已经完全忘光了。。。排列组合么?觉得没法算了,没办法回归本行 让电脑帮我算。。。
最开始用了一个穷举的算法 用了6层for循环 把结果求了出来 265 但是 这真的很汗 这种方法笨的可以 绝不是正道 其实这题的本质是一个叫欧拉错位排列的题 意思和上面的题目差不多 可以把它理解为 1——N个瓶子和1——N个瓶盖的错位匹配问题
这样一个问题用穷举是不可能得出每一个情况的答案的 必须换一个方法 我想到了回溯法 就是试探性的瓶子上放进瓶盖 不符合条件就换一个瓶盖 当无法进展下去的时候 倒退一步求解 这也是N皇后问题 以及四色问题的求解思路
于是我开始着手这样写 果然OK了 多说无益 详见源码
#include <conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int judge(int *a,int i,int j,int n) //判断瓶盖放在某瓶子上是否满足条件 满足则返回0 不满足返回1
{
if(j>n||i>n)
{
return 1;
}
if(a[i]==i)
{
return 1;
}
int k;
for(k=1;k<i;k++)
{
if(a[k]==j)
{
return 1;
}
}
return 0;
}
void out(int *a,int n) //输出函数
{
int i=0;
FILE *h=fopen("答案.txt","a");
for(i=1;i<=n;i++)
{
fprintf(h,"%d",a[i]);
fprintf(h,"%c",' ');
printf("%d",a[i]);
printf(" ");
}
fprintf(h,"%c",'/n');
printf("/n");
fclose(h);
}
int make(int *a,int n) //用回溯法计算的过程
{
FILE *h=fopen("答案.txt","a");
if(n<=1)
{
fprintf(h,"%d",0);
return 0;
}
memset(a,0,256);
int j=1,i=1,num=0;
while(1)
{
if(a[1]>n)
{
break;
}
if(j>n)
{
i--;
j=a[i]+1;
}
a[i]=j;
if(judge(a,i,j,n))
{
j++;
}
else
{
j=1;
i++;
}
if(i>n)
{
i=n;
j=a[i];
j++;
out(a,n);
num++;
}
}
return num;
}
int main()
{
int n=0;
int a[256]={0};
FILE *h=fopen("答案.txt","w");
fprintf(h,"%s","感谢绵羊同学使用");
fprintf(h,"%c",'/n');
fprintf(h,"%c",'/n');
fclose(h);
while(1)
{
puts("【题目】有标号1到N的N只瓶子。和1到N的盖子。它们没有任何数字相同的配对方式总共有多少?/n/n");
printf("请输入N的值(N的数值不要过大 否则算的会很慢——!):/n");
scanf("%d",&n);
FILE *h1=fopen("答案.txt","a");
if(n!=1)
{
fprintf(h1,"以上是当N等于%d时的答案",n);
fprintf(h1,"%c",'/n');
}
int num=make(a,n);
printf("符合要求的排列有:%d个/n/n",num);
printf("如果想换个N运算 摁回车继续.../n");
while(getchar()!='/n');
getch();
system("cls");
fclose(h1);
}
return 0;
}
写程序就是这样 遇到身边的疑难尝试着去解决 解决的多了 也就成长了