這一節以模擬題為主,比較惡心。一道動態規劃,一道枚舉。
Preface Numbering
問題描述:
先是介紹羅馬數字書寫(略),給出一個數字,求1到這個數字之間所有數字的羅馬表示中共出現幾個I,V,X,L,C,D,M。
分析:
最初的思路是一個個地模擬:觀察后可以發現,46可以表示為XLVI,以數組s[1…7]表示I…M,則47為2111000,而4為1100000,7為21000000,總結可知s[n][1]=s[n%10][1];s[n][2]=s[n%10][2];(注意n%10==9的話,s[n][3]+=s[9][3])。2<=i<=7時, s[n][i]=s[n/10][i-2]。
其實完全可以分段統計,比如234,個位出現了23個0~9,1個0~4,十位出現了20個0~9,1個0~3,百位出現了1、2;
Subset Sums
問題描述:
對于從1到N (1 <= N <= 39) 的連續整數集合,能劃分成兩個子集合,且保證每個集合的數字和是相等的。給出N,你的程序應該輸出劃分方案總數,如果不存在這樣的劃分方案,則輸出0。
分析:
看完題目最先想到的是深搜,枚舉所有可能,當其和等于總和一半的時候則記錄。但提交后發現,當N為31,程序算了7、8秒才出答案。看來深搜是不行了,在網上看了別人的解法才知道要動態規劃。其實應該算遞推,令ans[i][j]表示前i個數里面和為j的方案數,則ans[i][j]可以等于前i-1個數字里面和為j的方案數(即不含數字j的方案),加上前i-1個數字里和為j-i的方案數(即最終含有數字j,,ans[N][N*(N+1)/4]即為最終解。狀態轉移方程為:
ans[i][j]=ans[i-1][j]+ans[i-1][j-i];
Runaround Numbers
問題描述:
尋找循環數----從最右的數字出發,走數字對應的步數,然后從所停的數字繼續出發,再走剛才所停數字對應步數,直到回到出發點。
分析:
循環數有點類似約瑟夫環,直接模擬即可,從題目所給數字開始一個個枚舉。注意:數字不含0;回到起始點前,每一位都必須停留過一次且必須只停一次。
Party Lamps
問題描述:
有四個按鈕控制N盞燈,10<=N<=100
按鈕1:當按下此按鈕,將改變所有的燈:本來亮著的燈就熄滅,本來是關著的燈被點亮。
按鈕2:當按下此按鈕,將改變所有奇數號的燈。
按鈕3:當按下此按鈕,將改變所有偶數號的燈。
按鈕4:當按下此按鈕,將改變所有序號是3*K+1(K>=0)的燈。例如:1,4,7...
一個計時器記錄按下按鈕的次數C,現給出C,和某些燈的最終開關狀態,求所有的燈最終可能的所有狀態。
分析:
這題很久以前就見過了,覺得比較麻煩,就一直沒動,現在不動不行了。在紙上分析后會發現,燈的狀態是六個一組循環出現的,故只需得出前六個燈的狀態,后面的燈的狀態就可知了。每盞燈有開關兩種狀態,四個循環枚舉即可。在網上看了一下別人的解法,用二進制表示狀態是比較方便的,于是我決定第一次嘗試位運算解題。
由于異或運算符的特點:0和1異或1以后取反,異或0不變。第一種操作可以用n^(111111)2實現,第二種操作用n^(101010)2實現,第三種用n^(10101)2實現,第四種用n^(100100)2實現。
對于狀態的讀取,我想不出什么好辦法。只好這樣了:
int ReadOn()


{ bool tem[6];
memset(tem,false,sizeof(tem));
int i,c;
while(fin>>c&&c!=-1)
tem[(c-1)%6]=true;//此處燈亮,則令其為1
c=0;
for(i=0;i<6;i++)

{ c=c<<1;
if(tem[i])
c+=1;
}
return c;//c的二進制表示就是題目給出的部分燈亮的狀態
}

例如,測試數據給出2號燈亮,則最終狀態里2位上的必為1,表示為:010000 。讀入不亮的燈的函數做相應修改即可。
判斷當前枚舉得到的狀態是否滿足要求可用以下方法:
if(a+b+c+d<=times)
if((state|Off)==Off)

{ if((state&On)==On)//Off表示測試數據給出的不亮的燈 On表示亮的燈(即限制條件)
ans[state]=true;
if(state==0&&On==0)
ans[state]=true;
}

第五行如果state是0即都不亮,而On也為0,本來應該是符合的,但由于0&0=1!=0.所以在后面補上。