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語言的沒有報錯…
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章