题目大意
定义“反回文串”为一个字符串S[1..|S|],对于任意1≤i≤|S|都满足S[i]≠S[|S|-i+1]。很显然长度为奇数的字符串不可能是反回文串。
现给定字符串S,保证其长度为偶数。你需要把S的字符顺序打乱得到字符串T,满足它是反回文串,并且最大化其价值。价值定义为:所有满足S[i]=T[i]的位置i的b[i]之和(b数组给定)
|S|≤100
字符集为小写字母
分析
这种问题一般考虑网络流。
每个字符开一个点与S连,容量为S中的出现次数。每个位置向T连,容量为1。对于“反回文串”的限制,只需对于一个字符c,新开一个点(c,i) [i*2≤|S|] ,并且c向其连容量为1的边,然后(c,i)向位置i,|S|-i+1都连边,这就满足的限制。
这题需要最大化价值,可以把所有价值都加起来,然后对于c≠S[i],c向i连的边费用为b[i]。然后跑最小费用最大流即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=26*50+205,M=N*22,INF=1e9;
typedef long long LL;
int n,h[N],now[N],tot,e[M],nxt[M],co[M],fl[M],T,f[N],calc,vis[N],ans,b[N],cnt[N],I[N],Id[30][105],Idd[N];
char S[N];
void Add(int x,int y,int f,int c)
{
e[++tot]=y; nxt[tot]=h[x]; fl[tot]=f; co[tot]=c; h[x]=tot;
}
bool aug(int x)
{
vis[x]=calc;
if (x==T) return 1;
for (int i=now[x];i;i=nxt[i]) if (fl[i]>0 && vis[e[i]]<calc && f[x]==f[e[i]]+co[i])
{
if (aug(e[i]))
{
fl[i]--; fl[i^1]++; now[x]=i; return 1;
}
}
return now[x]=0;
}
void flow()
{
for (int i=0;i<=T;i++) now[i]=h[i];
for (;;)
{
calc++;
if (!aug(0)) return;
ans-=f[0];
}
}
bool check()
{
int tmp=INF;
for (int i=0;i<=T;i++) if (vis[i]==calc)
{
for (int j=h[i];j;j=nxt[j]) if (fl[j]>0 && vis[e[j]]<calc) tmp=min(tmp,f[e[j]]+co[j]-f[i]);
}
if (tmp==INF) return 0;
for (int i=0;i<=T;i++) if (vis[i]==calc) f[i]+=tmp;
return 1;
}
int main()
{
scanf("%d%s",&n,S+1);
for (int i=1;i<=n;i++)
{
scanf("%d",&b[i]); ans+=b[i]; cnt[S[i]]++;
}
for (int i=0;i<26;i++)
{
I[i]=++T;
for (int j=1;j*2<=n;j++) Id[i][j]=++T;
}
for (int i=1;i<=n;i++) Idd[i]=++T;
T++; tot=1;
for (int i=0;i<26;i++) if (cnt[i+'a'])
{
Add(0,I[i],cnt[i+'a'],0); Add(I[i],0,0,0);
for (int j=1;j*2<=n;j++)
{
Add(I[i],Id[i][j],1,0); Add(Id[i][j],I[i],0,0);
Add(Id[i][j],Idd[j],1,(S[j]!=i+'a')*b[j]); Add(Idd[j],Id[i][j],0,-(S[j]!=i+'a')*b[j]);
Add(Id[i][j],Idd[n-j+1],1,(S[n-j+1]!=i+'a')*b[n-j+1]);
Add(Idd[n-j+1],Id[i][j],0,-(S[n-j+1]!=i+'a')*b[n-j+1]);
}
}
for (int i=1;i<=n;i++)
{
Add(Idd[i],T,1,0); Add(T,Idd[i],0,0);
}
for (flow();check();flow());
printf("%d\n",ans);
return 0;
}