藍橋杯練習題---完美的代價(基於貪心演算法)
有一段時間沒有用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;
}
這樣問題就解決了,先忽略那個特殊字元不進行處理的方法的確挺巧妙,值得一記。