[codeforces884F]Anti-Palindromize

题目大意

定义“反回文串”为一个字符串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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值