【发布时间】:2011-08-25 21:25:36
【问题描述】:
几天来,我一直在努力解决这个问题,并在网上彻底搜索了有关如何解决它的任何提示。如果您喜欢面向数学的编程问题,请看一看!
这里是problem (PDF courtesy of UVA):
考虑一个由 n 个整数组成的序列 。由于所有的值都是不同的,我们知道有 n 个阶乘排列。如果每个元素的原始位置和新位置之间的绝对差最大为 K,则称为 K 变换排列。给定 n 和 K,您必须找出 K 变换排列的总数。
...
输入: 输入的第一行是一个整数 T (T
输出: 对于每个案例,首先输出案例编号,然后是所需的结果。由于结果可能很大,所以输出结果模 73405。
问题设置者Sohel Hafiz 将此问题归类为“Fast Matrix Exponentiation”。不幸的是,我在这里链接的谷歌搜索似乎没有显示任何相关链接,除了一个充满数学术语和符号的维基百科页面(我已经证明,维基百科是任何数学教科书的糟糕替代品)。
这是我到目前为止所做的:
此代码将通过递归计算低 n 和 k 值的 K 变换排列的数量,但过于复杂。建立一个用于搜索模式的表格就足够了:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int permute(int * a, int size, int i, int k)
{
int j;
int total = 0;
int x = size-i;
int low=0;
int high=size;
if (i == 0)
{
/* for (j=0;j<size;j++)
printf("%d, ", a[j]);
printf("\n");
*/ return 1;
}
if (x-k>0)
low = x-k;
if (x+k+1<size)
high = x+k+1;
for (j=low;j<high;j++)
{
int b[size];
memcpy(b,a,size*sizeof(int));
if (b[j] == 0)
{
b[j] = x+1;
total += permute(b,size,i-1,k);
}
}
return total;
}
int main()
{
int n, k, j, y, z;
int * arr;
/*scanf("%d %d", &n,&k);*/ k=2;
for (n=0;n<14;n++)
{
int empty[n];
for (j=0;j<n;j++)
empty[j] = 0;
arr = empty;
z = permute(arr, n, n, k);
y = magic(n,k);
printf("%d %d\n",z, y);
}
return 0;
}
我首先想到的是 k=1 显然是斐波那契数列。 main 中的魔法函数是我后来发现的,几乎是偶然的。它仅适用于 k=2,但精确到 n=14。
int magic(int n, int k)
{
if (n<0)
return 0;
if (n==0)
return 1;
if (n==1)
return 1;
return 2*magic(n-1,k) + 2*magic(n-3,k) - magic(n-5,k);
}
很奇怪!我不知道这个函数的意义,但它可以被简化为在一个循环中运行,以便运行得足够快,以完成 K=2 的值高达 10^9。
剩下的就是找到一个非递归方程,它可以在合理的时间内(10 秒以内)找到 K=3 的任何值。
编辑:我对用于在合理时间内解决任何给定 n 和 k 问题的算法感兴趣。我不希望任何人通过根据比赛规则的规范编写代码来真正确认他们的算法是否有效,我在答案中寻找的是关于如何解决问题并应用数值方法来达到解决方案的描述。
【问题讨论】:
-
@MAK:我认为鉴于标题中的明确声明以及 OP 已经付出了一些努力自己寻找解决方案的事实,这不算作弊。有时了解给你的解决方案确实有助于学习新的角度来看待问题。
-
@MAK 好点,问题的最后一行写得很仓促。我已经替换它以澄清我在答案中寻找的内容(当时我没有意识到解决方案的复杂性)。到目前为止,MizardX 有最好的答案并且它没有放弃解决方案。一旦我实施他的矩阵算法以确认它在合理的时间内工作,我计划奖励他。我在犹豫是否要在 C 中尝试,所以我必须用 Java 重写它。
-
@Tozar:太好了。我已经删除了我的 cmets。祝你好运。
标签: algorithm math complexity-theory permutation