NOIP 2003 數字遊戲
題目
題目描述
丁丁最近沈迷於一個數字遊戲之中。這個遊戲看似簡單,但丁丁在研究了許多天之後卻發覺原來在簡單的規則下想要贏得這個遊戲並不那麽容易。遊戲是這樣的,在你面前有一圈整數(一共 nnn 個),你要按順序將其分為 mmm 個部分,各部分內的數字相加,相加所得的 mmm 個結果對 101010 取模後再相乘,最終得到一個數 kkk 。遊戲的要求是使你所得的k最大或者最小。
例如,對於下面這圈數字( n=4,m=2n=4,m=2n=4,m=2 ):
要求最小值時, ((2?1)mod10)×((4+3)mod10)=1×7=7((2-1) \bmod 10)×((4+3) \bmod 10)=1×7=7((2?1)mod10)×((4+3)mod10)=1×7=7 ,要求最大值時,為 ((2+4+3)mod10)×(?1mod10)=9×9=81((2+4+3) \bmod 10)×(-1 \bmod 10)=9×9=81((2+4+3)mod10)×(?1mod10)=9×9=81 。特別值得註意的是,無論是負數還是正數,對 101010 取模的結果均為非負值。
丁丁請你編寫程序幫他贏得這個遊戲。
輸入輸出格式
輸入格式:輸入文件第一行有兩個整數, n(1≤n≤50)n(1≤n≤50)n(1≤n≤50) 和 m(1≤m≤9)m(1≤m≤9)m(1≤m≤9) 。以下 nnn 行每行有個整數,其絕對值 ≤104 \le 10^4≤104 ,按順序給出圈中的數字,首尾相接。
輸出格式:輸出文件有 222 行,各包含 111 個非負整數。第 111 行是你程序得到的最小值,第 222 行是最大值。
輸入輸出樣例
輸入樣例#1:4 2 4 3 -1 2輸出樣例#1:
7 81
分析
這題用了兩個方法來做,第一個做出來的是DFS,就先講講搜索的做法。
開始之前要先說這道題的基本思路,很顯然這道題是和環形問題和有關的,那麽我們對於環形一類的問題有一個通用做法:斷環為鏈(或者類似的叫法,whatever)。也就是把整個數組復制一遍,然後以每一個點作為起點,及其以後的(n-1)個點,作為範圍,其實相當於,我們用一個1到n的循環先指定了這個數組第一個斷開的地方。
這道題是可以用深搜來做的,但是要保持時刻的頭腦清晰才行,有點略微搞腦子。
搜索函數包含了四個參數值,我分別稱為dep, now, start, x。分別代表了,接下來要切割的次數(搜索的深度或者層數)、當前處理到的位置、當前情況鏈的初始點、當前值。重點在於剪枝。這道題是一個基礎的可行性剪枝(一開始怎麽沒想到……),簡單來說,如果當前搜索到的值已經不可能再給這道題目帶來更優的值(最大/最小值),那麽剪枝,舍去接下來的搜索,否則搜索會變成一個龐大的量。遞歸的時候,我們在now之後的點裏面找符合條件的點作為下一個切割點,但也要註意這個點要使得之後的切割點還有地方放(循環的判斷)。
最後的最後,有一個我把數據拿下來跑才找到的大坑,不然估計這輩子都過不了……當切割次數為1的時候,實際上就是求和。但如果正常進入循環搜索,就會因為剩余切割次數為0而直接跳出。處理方法兩種,把判斷往後拿,拿到函數最後,或者是在程序一開始就特判m的值。
另外一個就是DP,DP的程序難度不大,長度跟DFS也差不多。思考DP可以說是一勞永逸,只要寫出了公式,之後就是打打代碼的事情。所以終點說說這個公式。題解裏有很多人寫五層循環,其實沒有必要,我們可以略微簡化一點。
簡單來說,不用枚舉兩側分成了多少個部分,因為這會被之前或之後的循環枚舉到,打個比方:如果枚舉到了左端點是 i,右端點是 j,斷點是 k,左邊分成了p 個部分,右邊分成了 q 個部分。
f[i][j][p+q] = max f[i][j][p+q?1]?(S[j]?S[k])
最後的公式長這樣:
fmx[l][r][i] = max(fmx[l][r][i], fmx[l][k][i-1]*mod(S[r]-S[k]));
程序
1 #include <bits/stdc++.h> 2 using namespace std; 3 int fmx[200][200][20], fmn[200][200][20], n, m, S[150], a[150], ans_min, ans_max; 4 int mod(int x) 5 { 6 return (x % 10 + 10) % 10; 7 } 8 int main() 9 { 10 cin >> n >> m; 11 for (int i = 1; i <= n; i++) 12 { 13 cin >> a[i]; 14 a[i+n] = a[i]; 15 } 16 for (int i = 1; i <= n*2; i++) 17 S[i] = S[i-1] + a[i]; 18 for (int i = 1; i <= (n<<1); i++) 19 for (int j = i; j <= (n<<1); j++) 20 fmx[i][j][1] = fmn[i][j][1] = mod(S[j]-S[i-1]); 21 for (int i = 2; i <= m; i++) 22 for (int l = 1; l <= (n<<1); l++) 23 for (int r = l+i-1; r <= l+n-1; r++) 24 { 25 fmn[l][r][i] = 0x3F3F3F3F; 26 for (int k = l+i-2; k < r; k++) 27 { 28 fmx[l][r][i] = max(fmx[l][r][i], fmx[l][k][i-1]*mod(S[r]-S[k])); 29 fmn[l][r][i] = min(fmn[l][r][i], fmn[l][k][i-1]*mod(S[r]-S[k])); 30 } 31 } 32 ans_max = 0; 33 ans_min = 0x3F3F3F3F; 34 for (int i = 1; i <= n; i++) 35 { 36 ans_max = max(ans_max,fmx[i][i+n-1][m]); 37 ans_min = min(ans_min,fmn[i][i+n-1][m]); 38 } 39 cout << ans_min << endl << ans_max << endl; 40 return 0; 41 }
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAXN = 150; 4 int min_ans = 10000000, max_ans, n, m, S[MAXN], a[MAXN]; 5 int power[10] = {1, 9, 81, 729, 6561, 59049, 531441, 4782969, 43046721, 387420489}; 6 void dfs(int dep, int now, int start, int x) 7 { 8 if (dep > m) 9 { 10 x = x*((S[start+n-1]-S[now-1]+1000000)%10); 11 min_ans = min(min_ans, x); 12 max_ans = max(max_ans, x); 13 } 14 if (x >= min_ans && x * power[m-dep+2] <= max_ans) 15 return; 16 if (dep > m || now > start+n-1) 17 return; 18 for (int i = now; i <= start+n-2-(m-dep); i++) 19 dfs(dep+1, i+1, start, max(1,x)*((S[i]-S[now-1]+1000000)%10)); 20 } 21 int main() 22 { 23 S[0] = 0; 24 cin >> n >> m; 25 m--; 26 for (int i = 1; i <= n; i++) 27 { 28 cin >> a[i]; 29 a[n+i] = a[i]; 30 } 31 for (int i = 1; i <= 2*n; i++) 32 S[i] = S[i-1] + a[i]; 33 if (!m) 34 { 35 cout << (S[n] % 10 + 10) % 10 << endl << (S[n] % 10 + 10) % 10 << endl; 36 return 0; 37 } 38 for (int i = 1; i <= n; i++) 39 dfs(1,i,i,0); 40 cout << min_ans << endl << max_ans << endl; 41 return 0; 42 }
NOIP 2003 數字遊戲