知識準備: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步的最優解(先上圖容易理解)。
-
前面的數字代表圖中狀態。
- (參考圖右)首先考慮0、1、2、3四位的放置,不考慮四位以上。 狀態0:即首位不能是0(題意),不可以放置1(如果放置1,0肯定在1後面,所以不可以),2可以,不可以放置3(如果放置3,2就沒有地方放了)所以第一位只能是2。
- 現在我們來接着狀態0繼續:我們現在成功放了2了,那麼剩下0、1、3。接着可以放置0,不可以放置1(因爲放置1之後,0就沒放放了),得到狀態1——放0;可以放置3,所以我們獲得了狀態2——放0/3
- 接着狀態1,我們放置了0,那麼剩下1、3,201?可以,203?也可以,得到狀態3
- 接着狀態1,我們放置了3,那麼剩下0、1,230?可以,231?步可以,得到狀態4
- 我們回過頭再考慮狀態4,(狀態4是由狀態2來的),230?可以,231?不可以
- 最後狀態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();
}
}