CCF_2013-12-03有趣的数

知识准备:JAVA,贪心算法,动态规划

有趣的书问题描述

问题分析

1.使用暴力破解的方法计算的话,极有可能超时以及精度问题(自己写了一下只能写到9,而且精度直接就超了。。),所以只能使用动态规划

public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		long n = sc.nextLong();
		long t1 = System.currentTimeMillis();
		long count =0;
		long i,pCount =(long) Math.pow(10,n),v4=0,ci=0,startIdx = (long) Math.pow(10,n-1);
		System.out.println(startIdx+" TO "+pCount);
		
		for (i = 0; v4<pCount;i++) {
			v4=Integer.valueOf(new BigInteger(Long.toString(i), 10).toString(4));
			String vn = String.valueOf(v4);
			String[] vna = vn.split("");
			int[] pres = new int[vna.length];
			for (int j = 0; j < vna.length; j++) {
				pres[j] = Integer.parseInt(vna[j]);
			}
			boolean isTrue = judgeData(pres);
			if(isTrue && v4 > startIdx) {
				count++;
			}				
		}
		long t2 = System.currentTimeMillis();
		
		System.out.println("计算次数:"+ i+"\t耗时"+(t2-t1));
		System.out.println(count);
	}
		
	public static boolean judgeData(int[] tmp) {
		if(tmp[0]==0) return false;
		int t1 =-1,t3=-1;
		boolean b0=false,b1=false,b2=false,b3=false;
		boolean f0=true,f1=true,f2=true,f3=true;
		for (int i = 0; i < tmp.length; i++) {
			if(tmp[i]==1 && f1) {
				t1 = i;
				b1 =true;
				f1 = false;
			}
			if(tmp[i]==3 && f3) {
				t3 = i;
				b3 = true;
				f3 = false;
			}
			if(tmp[i]==0 && f0) {
				b0 = true;
				f0 = false;
			}
			if(tmp[i]==2 && f2) {
				b2 = true;
				f2 = false;
			}
		}
		if(t1==-1 || t3 ==-1 || !b0|| !b1|| !b2|| !b3) return false;
		for (int i = t1+1; i < tmp.length; i++) {
			if(tmp[i]==0) {
				return false;
			}
		}
		for (int i = t3+1; i < tmp.length; i++) {
			if(tmp[i]==2) {
				return false;
			}
		}
		return true;
	}

继续分析:只能选取动态规划:

动态规划的思路:前n步所有的最优解=前n-1步所有的最优解+第n步的最优解(先上图容易理解)。

在这里插入图片描述

  • 前面的数字代表图中状态。

  1. (参考图右)首先考虑0、1、2、3四位的放置,不考虑四位以上。 状态0:即首位不能是0(题意),不可以放置1(如果放置1,0肯定在1后面,所以不可以),2可以,不可以放置3(如果放置3,2就没有地方放了)所以第一位只能是2。
  2. 现在我们来接着状态0继续:我们现在成功放了2了,那么剩下0、1、3。接着可以放置0,不可以放置1(因为放置1之后,0就没放放了),得到状态1——放0;可以放置3,所以我们获得了状态2——放0/3
  3. 接着状态1,我们放置了0,那么剩下1、3,201?可以,203?也可以,得到状态3
  4. 接着状态1,我们放置了3,那么剩下0、1,230?可以,231?步可以,得到状态4
  5. 我们回过头再考虑状态4,(状态4是由状态2来的),230?可以,231?不可以
  6. 最后状态5,可以由状态3添3或者状态4添1得到

  总结:第一步骤:2          剩余 0 1 3
     第二步骤:2 0     剩余 1 3
     第三步骤:2 3     剩余 0 1
     第四步骤:2 0 1    剩余 3
     第五步骤:2 0 3    剩余 1
     第六步骤:2 0 3 1     剩余 无

(参考图左)考虑四位以上,第i位出现第j种状态的情况数表示为 a[i][j]
i位数的来源在于(i-1)位数(已经按顺序排列)+最后一位数字
例如:
  a[i][0] = a[i-1][0] ;
      使用过2 的情况(状态0)可以由: 1>状态0添2而来
                          2>前i-1位添加2而来(但是只能写2)
  a[i][1] = (a[i-1][0] + a[i-1][1] *2);
      使用过 2 0 的情况(状态1)可以由:1> 状态0添加0而来
                           2> 前i-1位(如当前位为5,i-1位就是4位)使用2 0,第i位使用0或2 (顺序正确)
  a[i][2] = (a[i-1][0] + a[i-1][2] );
      使用过 2 3 的情况(状态2)可以由:1> 状态0添加3而来
                           2> 前i-1位使用2 3,第i位使用3 (因为已经有了3了不能用2了,只能用3(因为从4位的时候这个地方就是3,并且只能是3))
  a[i][3] = (a[i-1][1] + a[i-1][3] *2);
      使用过 2 0 1 的情况(状态3)可以由:1> 状态1添加1而来
                           2> 前i-1位使用2 0 1,第i位使用1或2 (因为已经有了1了不能用0了,只能用1)
  a[i][4] = (a[i-1][1] + a[i-1][2] + a[i-1][4] *2);
      使用过 2 0 3 的情况(状态4)可以由: 1> 状态1添加3而来
                           2> 状态2添加0而来
                           3> 前i-1位使用2 0 3,第i位使用0或3 (因为已经有了3了不能用2了,只能用3)
  a[i][5] = (a[i-1][3] + a[i-1][4] + a[i-1][5] *2);
      使用过2 3 0 1的情况(状态5)可以由: 1> 状态3添加3而来
                           2>状态4添加1而来
                           3> 前i-1位使用2 3 0 1(状态6),第i位使用1或3
                     (因为已经有了1了不能用0了,只能用1)
                     (因为已经有了3了不能用2了,只能用3)
题目要求的答案为a[n][5],即0 1 2 3都使用过。

即JAVA版的代码:

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		long count = in.nextLong();
		int MOD=1000000007;
		long[][] sa = new long[(int) (count+1)][6];
		sa[0][0] =0;
		//由每一步的最优解求得
		for (int i = 1,j=0; i <= count; i++,j++) {
			sa[i][0] = 1;
			sa[i][1] = (sa[j][0]+sa[j][1]*2)%MOD;
			sa[i][2] = (sa[j][0]+sa[j][2])%MOD;
			
			sa[i][3] = (sa[j][1]+sa[j][3]*2)%MOD;
			
			sa[i][4] = (sa[j][1]+sa[j][2]+sa[j][4]*2)%MOD;
			sa[i][5] = (sa[j][3]+sa[j][4]+sa[j][5]*2)%MOD;
		}
//		System.out.println(sa[(int) count][5]);
		int location = 1;
		for (int i = 0; i < sa.length; i++) {
			if(i==0) continue;
			System.out.print(location++ +"位:\t");
			for (int j = 0; j < sa[i].length; j++) {
				System.out.print("【状态:"+j+":\t"+sa[i][j]+"】\t");
			}
			System.out.println();
		}
	}
但是这几天,提交之后总是抽风,系统总是编译出错.C语言的没有报错…
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章