1. 程式人生 > >藍橋杯練習題---完美的代價(基於貪心演算法)

藍橋杯練習題---完美的代價(基於貪心演算法)

有一段時間沒有用c++寫過程式了,偶爾心血來潮刷兩道水題,沒想到居然被一道題卡住了。。。。很難受emmmm

寫出來給大家分享下,覺得這道題挺精妙的,有值得學習的地方(大神可忽略)

題目如下:

問題描述  迴文串,是一種特殊的字串,它從左往右讀和從右往左讀是一樣的。小龍龍認為迴文串才是完美的。現在給你一個串,它不一定是迴文的,請你計算最少的交換次數使得該串變成一個完美的迴文串。
  交換的定義是:交換兩個相鄰的字元
  例如mamad
  第一次交換 ad : mamda
  第二次交換 md : madma
  第三次交換 ma : madam (迴文!完美!)輸入格式  第一行是一個整數N,表示接下來的字串的長度(N <= 8000)
  第二行是一個字串,長度為N.只包含小寫字母輸出格式  如果可能,輸出最少的交換次數。
  否則輸出Impossible樣例輸入5
mamad樣例輸出3

看上去並不難,一開始我的思路也正確,先假設前面的不動,從後往前遍歷字串,遇到相同的字元則移動到對應位置,並記錄下移動的次數。

可是我卻被中間那個字元卡住了,字串長度是偶數很好辦,奇數則會多出一個字元不需匹配,放在中間即可,原本我的思路是先給一次機會,遇到匹配不成功的則移到中間(奇數情況下),可是接下來繼續匹配的時候又出了問題,由於操作字串,那個單獨的字元產生了偏移,不在中間了。

於是我又把它操作了回去,結果一來一回多走了兩次。。。

好在最終我想出了辦法,就是先忽略這個特殊字元,繼續匹配直到完成,最後再來處理它,程式碼如下:

#include<cstdio>
#include<string>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cmath>
using namespace std;
int main()
{
 string str;
 int n,j,cnt=0,flag=0,index;          //cnt為移動次數,flag記錄唯一一次不匹配的機會
 cin>>n>>str;
 int last=n-1;                 //last很關鍵,記錄了未完成匹配那部分字串的末位
 for(int i=0;i<last;i++)
 {
     for(j=last;j>i;j--)
     {
         if(str[i]==str[j])
         {
         for(int u=j;u<last;u++)
         str[u]=str[u+1];
        str[last]=str[i];
        cnt+=last-j;
        --last;
        break;
         }
     }
     if(j==i&&(n%2==0||flag==1))   //如果匹配不到且(字串長度為偶數或者機會用完),那麼再見
     {
         cout<<"Impossible"<<endl;
         return 0;
     }
     else if(j==i)     //使用不匹配的那次機會,並記錄特殊字元的位置
     {
         flag=1;
         index=i;
     }
 }
 if(flag==0)
 cout<<cnt<<endl;
 else
 cout<<cnt+n/2-index<<endl;    //將不匹配的特殊字元移到中間(因為求次數所以直接修改cnt即可)
 return 0;
}

這樣問題就解決了,先忽略那個特殊字元不進行處理的方法的確挺巧妙,值得一記。