演算法第四章上機實踐報告

deerrrrr發表於2021-11-13

1.題目:刪數問題

給定n位正整數a,去掉其中任意k≤n 個數字後,剩下的數字按原次序排列組成一個新的正整數。對於給定的n位正整數a和正整數 k,設計一個演算法找出剩下數字組成的新數最小的刪數方案。如果數字最前面有0不輸出。

輸入格式:第 1 行是1 個正整數 a。第 2 行是正整數k。

輸出格式:輸出最小數。

    輸入樣例:                輸出樣例:

       

 

2.貪心選擇性質

2.1 思路

題目要求的是刪除k個數字之後的最小數,假設輸入序列的個數為n,本題可以轉化為:在輸入序列中取n-k個數構成最小數。我們每次取的構成最小數的數值就是當前序列的最小值,由於取出的數要按原次序排列,所以本題要考慮以下兩種情況:

①選擇當前序列的最小值後,最小值後面序列的個數小於我們還需要的數。這種情況我們就要捨棄掉第一小的數,選擇第二小的數判斷是否符合條件,若不符合繼續去第三小的數,以此類推,直到找到符合條件的數,break退出。

②選擇當前序列的最小值後,最小值後面序列的個數大於或等於我們還需要的數。這種情況當然就是我們需要的啦。

考慮以上兩種情況之後,還要注意,第一位數值若為0,不能輸出。

2.2 程式碼

#include <iostream>

#include <string.h>

#include <algorithm>

using namespace std;

char a[1000];

struct Num{

int arr;//數值

int brr;//下標

};//弄個結構體是為了快速排序的時候可以返回下標

Num num[1000];

Num num2[1000];//因為快速排序之後num會變成快排之後的序列,用num2保留原本序列

int k;

bool cmp(Num x,Num y){

if(x.arr==y.arr){

return x.brr<y.brr;

}

return x.arr<y.arr;

}

//快速排序

int vv(Num aa[],int start,int end,int min){

sort(aa+start,aa+end,cmp);

return aa[min].brr;

} //弄個min是為了能夠找到第二小、第三小...的數

int main() {

cin>>a;

cin>>k;

int n=strlen(a);//求出a的長度

bool cc=false;//一開始還沒輸出,初始值設為false

for(int i=0;i<n;i++){

num[i].arr=a[i]-48;

num[i].brr=i;

}//a是個字元陣列,這裡把它轉成整型陣列

for(int i=0;i<n;i++){

num2[i].arr = num[i].arr;

num2[i].brr = num[i].brr;

}//複製

int w=n-k;//需要找幾次

for(int i=0;i<w;i++){

int min = 0;

int result = vv(num,0,n,min);

//如果找到的數符合要求

if(n-1-result >= w-i-1){

//為0,但不是第一個

if(num2[result].arr==0&&cc==true){

cout << num2[result].arr;

}else{  //不為0

if((num2[result].arr)!=0){

cout << num2[result].arr;

cc=true;//記得設為已經有輸出了

}

}

n=n-result-1;//取得符合條件得數後,序列就變成該數後面的序列了

for(int ii=0;ii<n;ii++){

num[ii].arr = num2[ii+result+1].arr;

num[ii].brr = ii;

num2[ii].arr = num[ii].arr;

num2[ii].brr = num[ii].brr;

}

}else{//不符合要求,迴圈找到符合要求的,類似上面

for(min=1;min<n;min++){

int resultt = vv(num,0,n,min);

if(n-1-resultt >= w-i-1){

if(num2[resultt].arr==0&&cc==true){

cout<<num2[resultt].arr;

}else{

if((num2[resultt].arr)!=0){

cout<<num2[resultt].arr;

cc=true;

}

}

n=n-resultt-1;

for(int iii=0;iii<n;iii++){

num[iii].arr = num2[iii+resultt+1].arr;

num[iii].brr = iii;

num2[iii].arr = num[iii].arr;

num2[iii].brr = num[iii].brr;

}

break;

}

}

}

}

return 0;

}

3.時間複雜度分析

時間複雜度應該是O(n*(n-k))

理由:本題要找n-k次,找到合適的數值後都要遍歷複製陣列,平均時間複雜度為n/2,綜合起來時間複雜度為:O(n*(n-k))——取決於k的大小,若k很小的話,約等於O(n^2)

4.對貪心演算法的理解

在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,演算法得到的是在某種意義上的“區域性最優解”。

因此貪心演算法不是對所有問題都能得到整體最優解,關鍵是貪心策略的選擇。

5.疑難雜症

還有一種輸入方法,為什麼已經用long long int,足夠長了還不讓我過PINTIA,所有輸入樣例都過了,就是PINTIA不讓我過。

不過用long long int的方式定義a,輸入後再轉成陣列會導致倒序,程式碼寫起來有點複雜。以下就是long long int的方式定義a的程式碼:

#include <iostream>

#include <algorithm>

using namespace std;

long long int a;

long long int k;

struct Num{

int arr;//數值

int brr;//下標

};

Num num [1000];

Num num2 [1000];

bool cmp(Num x,Num y){

if(x.arr==y.arr){

return x.brr<y.brr;

}

return x.arr<y.arr;

}

//快速排序

int vv(Num aa[],int start,int end,int min){

sort(aa+start,aa+end,cmp);

return aa[min].brr;

}

int main() {

cin>>a;

cin>>k;

int n=0;

bool cc=false;

while(a!=0){

num[n].arr=a%10;

a=a/10;

n=n+1;

}//求得共有n個數

for(int i=n-1;i>=0;i--){

num[i].brr=n-1-i;

}

for(int i=0;i<n;i++){

num2[i].arr = num[i].arr;

num2[i].brr = num[i].brr;

}

//總共要找幾次

int w=n-k;

for(int i=0;i<w;i++){

int min=0;

int result = vv(num,0,n,min);

//如果找到的數符合要求

if(n-1-result >= w-i-1){

//為0,但不是第一個

if((num2[n-1-result].arr)==0&&cc==true){

cout << num2[n-1-result].arr;

}else{  

if((num2[n-1-result].arr)!=0){

cout << num2[n-1-result].arr;

cc=true;

}

}

for(int i=0;i<n-1-result;i++){

num[i].arr = num2[i].arr;

num[i].brr = n-result-2-i;

}

n=n-result-1;

}else{//不符合要求

for(min=1;min<n;min++){

int resultt = vv(num,0,n,min);

if(n-1-resultt >= w-i-1){

if((num2[n-1-resultt].arr)==0&&cc==true){

cout<<num2[n-1-resultt].arr;

}else{

if((num2[n-1-resultt].arr)!=0){

cout<<num2[n-1-resultt].arr;

cc=true;

}

}

for(int i=0;i<n-1-resultt;i++){

num[i].arr = num2[i].arr;

num[i].brr = n-resultt-2-i;

}

n=n-resultt-1;

break;

}

}

}

}

return 0;

}

相關文章