原题:
给定表达式[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%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); }
计算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); }
看来规律总结的不到位啊。
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位系统的验证一下吧。