原题:
给定表达式[x/2] + y + x * y, 其中x,y都是正整数。其中的中括号表示下取整,例如[3/2] = 1 , [5/2] = 2。
有些正整数可以用上述表达式表达出来,
例如正整数2,当取x = y = 1时,可以把2表达出来 ( 解释下:当x=y=1时, [x / 2] + y + x * y = [1 / 2] + 1 + 1 * 1 = 0+1+1 = 2 );
有些数可以有多种方式表达,例如13可以由 x = 2 y = 4 以及x = 3 y = 3来表示;
有些数无法用这个表达式表达出来,比如3。
从1开始第n个不能用这个表达式表示出来的数,我们叫做an,例如a1=1 a2=3,给定n,求an。
输入:n值 1<=n<=40 输出:an % 1000000007的结果(因为结果较大,输出an %1000000007的结果)。
个人分析:
表达式相当于 (x+1)*y+[x/2]
x=1...n 时,表达式分别为:
2*y 3*y+1 4*y+1 5*y+2 6*y+2 7*y+3 8*y+3 9y+4 10y+4 11y+5 12y+5 ...
这时我们分析:
第一个表达式为 偶数
第二个表达式为 an-1 整除4=0;
第三个表达式为 an-1 整除5=0;
。。。
由此类推
我们在判断 an 这个数是否为 不可表达的数时,应用如下判断
an%2!=0
如果an>1那么 (an-1)%3!=0 并且 因为x=4时,表达式相同 我们也应判断 (an-1)%4!=0
如果an>2那么 (an-2)%5!=0 并且 因为x=4时,表达式相同 我们也应判断 (an-2)%6!=0
.....
那么合适结束呢?判断到an/2,x固定时第一个表达式为最小值
那么我们的判断函数应为:
public static int givean(int n)
{
if (n < 1 || n > 40)
return -1;
double result = 0;
int countN = 0;
while (countN != n)
{
result += 1;
//* 以上相当于 (x+1)*y+[x/2]
// * 表达式为:x=1...n : 2*y 3*y+1 4*y+1 5*y+2 6*y+2 7*y+3 8*y+3 9y+4 10y+4 11y+5 12y+5 13+6 14+6 15+7 16+7 17+8 18+8
// * 在这里我们可以看出规律,
// 当an=3时 我们只需判断
// 1 an-2是否能被5,6整除
// 2 an-1 是否能被3,4整除
// 3 an 是否能被2整除
//.....如果符合上述条件则可以表达,如果不符合则不可表达
bool issuit = false;
for (double i = 0; i < result/2; i++)
{
double x = Math.Round(i*2, 0)+1;
if (Math.Abs(x - 1) > 0.00001d)
{
//eg: i=1 x=3 x1=4; i=2 x=5 x1=6;i=3 x=7 x1=8
double x1 = x + 1;
if (Math.Abs((result - i) % x - 0f) < 0.00001d || Math.Abs((result - i) % x1 - 0f) < 0.00001d)
{
issuit = true;
break;
}
}
else
{
if (Math.Abs(result % 2 - 0) < 0.00001d) //x=1 表达式为2*y 偶数全部可以表达
{
issuit = true;
break;
}
}
}
//
if(!issuit)
countN++;
}
return (int) (result % 1000000007);
}
得到的结果为: a1=1 ,a2=3,a3=15,a4=63,a5=4095,a6=65535,a7=262143,a8=...
计算a8就要很长时间了。
这个解决方案是否正确尚未可知,我提交上去代码后却提示:
另外再提交C#代码时,注意将类名改为其他,他与主函数重名会报错。。。
另外不知道庞果检测代码是如何进行的,判断正确与否是怎么个机制,应该是提取方法验证结果与时间吧,
挺有意思的,希望懂行的多交流交流。
看了一下,已经有16个大牛做出来了。
不过,这么多的问题没有答案什么的嘛,或者交流的地方。。。。
2013年8月13日6:38:20早晨 更新
晚上睡觉的时候想了想,只能优化算法了,目标 取a40 3s内。。。
再循环的时候可以减轻一些压力,首先【 前取整】 代表有连续的两个数相等,所以我们循环的步长可以是2
修改代码如下:
public static int givean(int n)
{
Stopwatch stp=new Stopwatch();
stp.Start();
if (n < 1 || n > 40)
return -1;
if (n == 1)
return 1;
double result = 1;
int countN = 1;
while (countN != n)
{
//x=1 表达式为2*y 偶数全部可以表达 所以我们支取基数
result += 2;
//* 以上相当于 (x+1)*y+[x/2]
// * 表达式为:x=1...n : 2*y 3*y+1 4*y+1 5*y+2 6*y+2 7*y+3 8*y+3 9y+4 10y+4 11y+5 12y+5 13+6 14+6 15+7 16+7 17+8 18+8
// * 在这里我们可以看出规律,
// 当an=3时 我们只需判断
// 1 an-2是否能被5,6整除
// 2 an-1 是否能被3,4整除
// 3 an 是否能被2整除
//.....如果符合上述条件则可以表达,如果不符合则不可表达
bool issuit = false;
//y=1,2....n : 1+x+[x/2] 2+2x+[x/2] 3+3x+[x/2] ...n+nx+a
//x的值取y=1时 的最大值为
double limit = result - 1;
//x=2,3...时的表达式判断[2/2]=[3/2] [x/2]=[x+1/2]所以x的步长为2,一次判断两个
for (double x = 2; x <= limit; x += 2)
{
//
//[x/2]
double yushu = Convert.ToInt64(x/2);
double a = x + 1;//系数
//
//limit = result - yushu - 1;
//
if (Math.Abs((result - yushu) % a - 0f) < 0.00001d ||
Math.Abs((result - yushu) % (a+1) - 0f) < 0.00001d)
{
issuit = true;
break;
}
}
if (!issuit)
{
countN++;
Console.WriteLine("N=" + countN + " an=" + result);
}
}
stp.Stop();
TimeSpan ts = stp.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
Console.WriteLine("Time taken : {0}", elapsedTime);
return (int) (result % 1000000007);
}
运行的结果是:
N=2 an=3
N=3 an=15
N=4 an=63
N=5 an=4095
N=6 an=65535
N=7 an=262143
Time taken?:?00:00:00.11
计算n=8时 需要5分多钟。。。。太让人郁闷了。
开来还是有优化的地方。。。。
2013年8月13日8:17:35 第二次更新
重新更改了一下:
这下取n=8 控制到5分钟以内了,代码如下:
public static int givean(int n)
{
Stopwatch stp=new Stopwatch();
stp.Start();
if (n < 1 || n > 40)
return -1;
if (n == 1)
return 1;
double result = 1;
int countN = 1;
while (countN != n)
{
//x=1 表达式为2*y 偶数全部可以表达 所以我们支取基数
result += 2;
//* 以上相当于 (x+1)*y+[x/2]
// * 表达式为:x=1...n : 2*y 3*y+1 4*y+1 5*y+2 6*y+2 7*y+3 8*y+3 9y+4 10y+4 11y+5 12y+5 13+6 14+6 15+7 16+7 17+8 18+8
// 2y+0 3y+1 4y+1 5y+2 6y+2 7y+3 8y+3 9y+4 10y+4
//11y+5 12y+5 13y+6 14y+6 15y+7 16y+7 18y+8 19y+8 20y+9 21y+9
//
//eg:result=5 5*2/3=3 (3+1)*1+1=5
long tmpx = Convert.ToInt64(result*2/3);
if ((tmpx + 1 + Convert.ToInt64(tmpx / 2)) == Convert.ToInt64(result))
continue;
//
// * 在这里我们可以看出规律,
// 当an=3时 我们只需判断
// 1 an-2是否能被5,6整除
// 2 an-1 是否能被3,4整除
//.....如果符合上述条件则可以表达,如果不符合则不可表达
bool issuit = false;
//y=1,2....n : 1+x+[x/2] 2+2x+[x/2] 3+3x+[x/2] ...n+nx+a
//x的值取y=1时 的最大值为
double limit = result - 1;
//x=2,3...时的表达式判断[2/2]=[3/2] [x/2]=[x+1/2]所以x的步长为2,一次判断两个
for (double x = 2; x <= tmpx; x += 2)
{
//
//此x值得表达式为 a * y + yushu
double yushu = Convert.ToInt64(x/2);
double a = x + 1; //系数
double a2 = x + 2;
//
//此时,如果系数与加数同为偶数时 可以不用判断
//
if (Math.Abs(yushu % 2 - 0) > 0.00001d || Math.Abs(a % 2 - 0) > 0.00001d)
{
if (Math.Abs((result - yushu)%a - 0f) < 0.00001d)
{
issuit = true;
//Console.WriteLine(x+"");
break;
}
}// Math.Abs((result - yushu)%(a + 1) - 0f) < 0.00001d
if (Math.Abs(yushu % 2 - 0) > 0.00001d || Math.Abs(a2 % 2 - 0) > 0.00001d)
{
if (Math.Abs((result - yushu) % a2 - 0f) < 0.00001d)
{
issuit = true;
//Console.WriteLine(x+1 + "");
break;
}
}
}
if (!issuit)
{
countN++;
Console.WriteLine("N=" + countN + " an=" + result);
}
}
stp.Stop();
TimeSpan ts = stp.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
Console.WriteLine("Time taken : {0}", elapsedTime);
return (int) (result % 1000000007);
}
想了一下,如果数据变大时比如10000001时,要验证的太多了,也即循环耗费时间太长了,
看来规律总结的不到位啊。
2013年8月14日15:05:09更新
看了http://blog.csdn.net/chengyxc/article/details/9958035 他这个算法似乎是对的,
符合的条件:n具有2^m-1的形式,并且,2n+1必定是个素数,这样的数才是不可能数
跟着更新了下算法。
但是 好像还是不能很好的实现。时间是个大问题,算到第21个数了,用时大约10s了。
肯定超限了。
算法如下:
Stopwatch stp=new Stopwatch();
stp.Start();
long inf = 140000000;// int.MaxValue;// 264800000;// 2147483637;
//List<int> prime = new List<int>();
int[] prime = new int[inf];
prime[0] = prime[1] = 0;
for (int i = 2; i < inf; ++i)
prime[i] = 1;
for (int i = 4; i < inf; i += 2)
prime[i] = 0;
double t = Math.Sqrt(inf);
for (int i = 3; i <= t; i += 2)
{
if (prime[i]>0)
{
int k = i * i, p = i + i;
for (int j = k; j < inf; j += p)
{
prime[j] = 0;
//if ((j - 1)%2 == 0)
// Console.WriteLine("j="+j+" "+(j - 1) /2 + "");
}
}
}
//以上得到了 1 -- inf 范围 中的所有素数,在素数中判断条件
int count = 1;
for (long i = 0; i < prime.LongLength; i++)
{
if (prime[i] != 0)
{
if ((i - 1)%2 == 0)
{
//符合2n+1为素数
double n = (i - 1)/2;
if (n < 3)
continue;
//符合2^M-1
double a = Math.Log(n + 1, 2); // Math.Log(2, n + 1);
double b = (a - Convert.ToInt64(a));
if (a != double.NaN && a > 1 && Math.Abs(a - Convert.ToInt64(a)) < 0.000001d)
{
count++;
//Console.WriteLine(string.Format("n={0} an={1}",count,n));
}
}
}
}
stp.Stop();
Console.WriteLine(stp.ElapsedMilliseconds/1000d+"S");
//
输出结果为:
n=2 an=3
n=3 an=15
n=4 an=63
n=5 an=4095
n=6 an=65535
n=7 an=262143
n=8 an=2097150
n=9 an=8388606
n=10 an=33554409
n=11 an=33554418
n=12 an=33554429
n=13 an=33554439
n=14 an=67108824
n=15 an=67108844
n=16 an=67108878
n=17 an=67108886
n=18 an=67108889
n=19 an=67108890
n=20 an=67108899
n=21 an=67108901
9.854S
我的是32位系统,数组最大长度只能设为:264800000 再长就报超出内存了。也不知道是不是系统的问题。64位系统的验证一下吧。