POJ1854
题意略。
思路:
贪心的想法是:
每次搞定最左边和最右边的两个字母,也就是从外向内一层层做成回文串。
比如 abcbac 这个,先看最左边的“a”,从最右边开始遍历字符串,找到的第一个“a”就可以经过最少次数把右边变成“a”,
再看最右边的“c”,同样的,从最左边遍历字符串,找到的第一个“c”就可以经过最少次数把右边变成“c”,
比较一下是把最外层变成两个a还是两个c……哪个划算,就加上这个步数,把字符串最外层排好:“abcbca”,then去掉最外层变成:“bcbc”,继续重复上面的工作……
我在做的时候想到了一个问题,如果当前最贪心的解既不在最左,也不在最右,而是中间的某个字母,此时我的贪心方法还正确吗?
我这里拿a,b举例:
假设a是最左边的,且我令取右端的永远不如取左端的优,而b是从a开始向右扫描第一个swap数比a小的:
我假设左端的a到b之间有i个字母(左闭右开),任取其中一个字母,记它的下标为j,以它作为区间两端的swap数为Kj,可知a的swap数为K1。
再记b作为区间两端的swap数为X,那么我们从a一直处理到b这个过程中我的swap总数为:
K1 + K2 + ...... + Ki + X - i - w0 - w1
这里说明一下:
我在从a处理到b之前的这i个字母swap的总数并不是K1 +... + Ki了,因为区间两端点一直在变化,实际的数字比这个加和要小,
由于左端点向中间靠近所带来的减损我们记为w0,由于右端点向中间靠拢所带来的减损我们记为w1。
注意,只要我是从1到i这个顺序来处理,那么这两个数字是不会发生变化的。
还有一个要知道的是,这i个字母它们在右端(从右向左扫描)的第一个相同字母一定在右端b的左边。
如果在右边,那么b作为“从a开始向右扫描第一个swap数比a小的”这么一个条件也就不成立了。
我们再来考虑一下处理前 i 个字母对b的影响:
首先,对于左端的b,它距离左端点的长度一直在减小,因此我们要 - i来表示。
对于右端点的b,它距离右端点的值始终没有变化,因为不断往中间靠拢的右端使用的字母来自b的左端。
因此上式成立。
如果我们此时先处理左端的b,再来处理1...i的话,由于b在1 ... i的右端,所以它对K1 + ... + Ki不会有额外的影响,
但是由于右端的b一定在这1...i这i个字母于右端相应位置的右边,所以它对这i个字母统一有一个右端点向中间靠拢1个单位的影响,
因为有i个字母,所以共计影响应该 - i。
这种情况下的swap总数记为:K1 + K2 + ...... + Ki + X - i - w0 - w1。
与上种策略一样,所以处理两端的是最优的。
但为什么要优先处理两端中swap数最小的呢?
因为越小说明它越能比别的字母靠近两端,越靠近两端,它带来的总体减损值也就越多。
代码附上:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int maxn = 8005; const int maxn0 = 505; int cnt[maxn0],T; char str[maxn]; int main(){ scanf("%d",&T); while(T--){ memset(cnt,0,sizeof(cnt)); scanf("%s",str); int len = strlen(str); for(int i = 0;i < len;++i) ++cnt[str[i]]; int odd = 0; for(int i = 'a';i <= 'z';++i) odd += (cnt[i] & 1); if(odd > 1){ printf("Impossible\n"); continue; } int ans = 0; int lft = 0,rht = len - 1; while(lft < rht){ int ptr0,ptr1,cost0,cost1; for(ptr0 = rht;ptr0 > lft && str[ptr0] != str[lft];--ptr0); for(ptr1 = lft;ptr1 < rht && str[ptr1] != str[rht];++ptr1); cost0 = ptr0 == lft ? maxn : rht - ptr0; cost1 = ptr1 == rht ? maxn : ptr1 - lft; if(cost0 < cost1){ for(int i = ptr0;i < rht;++i) swap(str[i],str[i + 1]); } else{ for(int i = ptr1;i > lft;--i) swap(str[i],str[i - 1]); } ans += min(cost0,cost1); lft += 1; rht -= 1; } printf("%d\n",ans); } return 0; }