CF884F Anti-Palindromize [费用流]

博客介绍了一种解决字符相关问题的方法。通过向每个字符连字符出现次数流量的边保证字符出现次数与原串相同,考虑对称位置不能相同,建 n / 2 个节点并让字符向其连边,根据不同情况设置边的费用,最后用最大费用最大流求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

S 向每个字符连字符出现次数流量的边, 这样就保证了字符出现次数与原串相同

考虑对称位置不能相同, 我们建 n / 2 个节点, 每个字符向这 n / 2 个节点连边

如果 s[i] 和 s[n-i+1] 都是当前字符, 那么只能满足一个, 费用连 max(v[i], v[n-i+1])

如果有一个为当前字符, 那么直接连费用为它的边

如果两个都不是, 那就是去用来调节的, 连一条费用为0的边, 最后最大费用最大流就可以了

#include<bits/stdc++.h>
#define N 100050
using namespace std;
int first[N], nxt[N], to[N], w[N], c[N], tot = 1;
void add(int x, int y, int z, int v){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z, c[tot] = v;
	nxt[++tot] = first[y], first[y] = tot, to[tot] = x, w[tot] = 0, c[tot] = -v;
}
int n, a[N], b[N]; char s[N];
int st, ed, t[N];
int dis[N], from[N], froms[N]; bool vis[N];
bool spfa(){
	memset(dis, 63, sizeof(dis)); int Inf = dis[0];
	memset(vis, 0, sizeof(vis));
	queue<int> q; q.push(st); dis[st] = 0; vis[st] = 1;
	while(!q.empty()){
		int x = q.front(); q.pop(); vis[x] = 0;
		for(int i=first[x];i;i=nxt[i]){
			int t = to[i]; 
			if(w[i] && dis[t] > dis[x] + c[i]){
				dis[t] = dis[x] + c[i]; from[t] = x; froms[t] = i;
				if(!vis[t]) vis[t] = 1, q.push(t);
			}
		} 
	} return dis[ed] != Inf;
}
int calc(){
	int flow = 0x3fffffff, u = ed;
	while(u){ flow = min(flow, w[froms[u]]); u = from[u];} u = ed; 
	while(u){ w[froms[u]] -= flow; w[froms[u] ^ 1] += flow; u = from[u];}
	return flow;
}
int dinic(){ int ans = 0; while(spfa()) ans += calc() * dis[ed]; return ans;} 
int main(){
	scanf("%d%s", &n, s+1); st = 0; ed = 26 + n / 2 + 1;
	for(int i=1; i<=n; i++) b[i] = s[i] - 'a' + 1, t[b[i]] ++;
	for(int i=1; i<=n; i++) scanf("%d", &a[i]);
	for(int i=1; i<=26; i++){
		if(!t[i]) continue;
		add(st, i, t[i], 0);
		for(int j=1; j<=n/2; j++){
			if(b[j] == i && b[n-j+1] == i) add(i, j + 26, 1, -max(a[j], a[n-j+1]));
			else if(b[j] == i) add(i, j + 26, 1, -a[j]); 
			else if(b[n-j+1] == i) add(i, j + 26, 1, -a[n-j+1]);
			else add(i, j + 26, 1, 0); 
		}
	} 
	for(int i=1; i<=n/2; i++) add(i + 26, ed, 2, 0);
	printf("%d\n", -dinic()); return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值