問題描述:
通過鍵盤輸入一個高精度的正整數n(n的有效位數≤240),去掉其中任意s個數字后,剩下的數字按原左右次序將組成一個新的正整數。編程對給定的n和s,尋找一種方案,使得剩下的數字組成的新數最小。
問題分析:
這個問題是最優子結構問題,即局部最優能決定全局最優解,可以使用貪心算法進行解決。n個正整數去掉s個數字,求使得到的新的正整數最大的刪除方案可以等價為:對於n個正整數組成的數字,一個一個地依次去掉s個數字,要求每刪除一個數時,都使刪除后的新的正整數最小。因此問題轉化為求解刪除一個數字時是新的數字最小的方案,求得這個方案后只需要對其執行s次即可。
因為刪數后剩下的數字原左右次序不變,所以要盡可能在左邊刪除,我們從左往右進行考慮,假設n個整數為a(1),a(2)....a(n),當由左往右第一次出現a(k)>a(k+1),可以發現,①刪除任何一個a(k)之前的元素得到的新數都比刪除a(k)得到的新數大,所以可以明確a(k)之前的元素可以排除;②倘若刪除a(k),則新的數字第k位變成a(k+1),記這個新的數字為c,倘若刪除a(k)之后的任何一位,得到的新的數字第k位仍然是a(k),記這個新的數字為d。比較c和d,可以發現,c(1)=d(1),......,c(k-1)=d(k-1),因為a(k)>a(k+1),所以c(k)<d(k),那么有c<d。綜上可知,對於數字a,應該刪除由左往右第一次出現a(k)>a(k+1)時的a(k),如果a的數字序列一直是非減的,那么刪除最后一位即可。
本算法的完整C++代碼如下:
#include "stdafx.h" #include "vector" #include "cmath" #include "iostream" using namespace std; int digit_to_int(char d)//將char數組中的元素轉換為整數以便於進行比較 { char str[2]; str[0] = d; str[1] = '/0'; return (int)strtol(str, NULL, 10); }
void DeleteOneBit(char * a,int _size){//每次只刪除一位的函數 int i = 0; bool _is = false;//用於判斷是否需要將部分右邊的元素左移一位 while (i < _size - 1){ if (digit_to_int(a[i])>digit_to_int(a[i+1])){ _is = true; break;} i++; } if (_is){ for (; i < _size - 1;i++) { a[i] = a[i + 1]; } } a[_size - 1] = '\0';//寫終止符 }
void GetNewNum(char* b,int _num_d){ int _lenght = strlen(b); if (_num_d>=_lenght){ cout << "刪除的位數過多" << endl; return;} for (int i = 0; i < _num_d;i++){ DeleteOneBit(b, _lenght); _lenght--; } } int _tmain(int argc, _TCHAR* argv[]) { cout << "請輸入要進行刪數的正整數(小於240位)"<<endl; char a[241]; cin >> a; int _num_d; cout << "請輸入要刪除的位數(小於輸入的整數的位數)" << endl; cin >> _num_d; GetNewNum(a, _num_d); cout << a; system("pause"); return 0; }