1.問題描述
現有一個 n 位數,你需要刪除其中的 k 位,請問如何刪除才能使得剩下的數最大?
比如當數為 2319274, k=1 時,刪去 2 變成 319274 後是可能的最大值。
2.問題分析
[1]貪心解法
這題可以使用貪心策略,每次從高位向低位數,刪除高位比低位數字小的那位上的數字,直到刪除了k位之後,得到的數字肯定是最大值。
(1)刪數問題具有最優子結構:
(2)刪數問題具有貪心選擇性質:
設問題T已按照上面的方法刪除,假設 A=(y1,y2,···,yk) 是刪數問題的一個最優解。易知,若問題有解,則1≤k≤n。 (1)當k=1時,由前得證,A=(y1,A′)是問題的最優解,其中A′是A中不刪除了y1而刪除其他位的最優解; (2)當k=q時,由反證法,可得A=(y1,y2···,yq)是最優解; 當k=q+1時,由前得證,A=(y1,y2···,yq+yq+1)是最優解。 所以,刪數問題具有貪心選擇性質。
程式碼很容易實現,AC,1.484s,1.089MB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <string> #include <iostream> using namespace std; int t,k,len; string name; void deletek(){ int tlen=name.length(); int tk=k; bool flag=true; while (k--> 0 && flag) { flag=false; len = name.length(); for (int i=0; i<len; i++) { if (i+1<len && name[i]<name[i+1]) { name.erase(i,1); len--; flag=true; break; } } } cout << name.substr(0,tlen-tk) << endl; } int main(int argc, const char * argv[]) { cin >> t; while (t-->0) { cin >> name; cin >> k; deletek(); } return 0; } |
[2]動態規劃解法
根據上面的分析可以看出此題還可用動態規劃來解決,思路如下:
假設A(i,j)表示輸入數字(字串)的從第i位到第j位數字組成的字串,S(i,j)表示前i位中刪除j位得到的最優解,它實際上可以看做兩個子問題:如果刪除第j位,那麼S(i,j)等於前i-1位刪除j-1位的最優解加上第j位數字;如果不刪除第j位,那麼S(i,j)等於前i-1位刪除j位的最優解。於是便有下面的遞推式:
這個遞推式非常類似最長公共子序列問題的遞推式,所以解法也類似,在空間方面可以只使用一個一維陣列,加上一個額外的O(1)的空間,計算過程如下面製作的表格所示,除了第一列,其他中間元素都只依賴於上面一行對應位置S(i−1,j)和上面一行左邊位置S(i−1,j−1)兩個元素的大小,比較的是字串,使用字典序進行比較,C++內建的字串比較函式compare即可。
動態規劃實現程式碼 [這份程式碼沒有AC,只能得到60分就超時了,應該還可以改進]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <string> #include <iostream> using namespace std; #define MAX_K 1001 int t,k; string name;string up;string last;string temp; void deletek(){ int len=name.length(); if(k>=len){ cout << "" << endl; return; } string cur[MAX_K]={""}; for (int i=1; i <= len; i++) { for (int j=0; j < i && j <= k; j++) {// if (j==0) {//sub string last=cur[j]; cur[j]=name.substr(0,i); }else{//0 < j <= i up=cur[j]+name[i-1];// if (up.compare(last)>=0) {//up > left last=cur[j]; cur[j]=up; }else{//up < left temp=cur[j]; cur[j]=last; last=temp; } } } } cout << cur[k] << endl; } int main(int argc, const char * argv[]) { cin >> t; while (t-->0) { cin >> name; cin >> k; deletek(); } return 0; } |
從這道題中可以看出,雖然動態規劃每次做出當前情況下最好的決策,但是為了做出最好的決策花費了大量的時間和空間,對於刪數問題貪心演算法應該是較好的解決方案。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!