[AGC030E] Less than 3 题解
题目链接 : https://atcoder.jp/contests/agc030/tasks/agc030_e
题意:
给定两个长度为 \(N\) 的 01 串,串的连续段长度不超过 \(2\) ,每次操作可以把第一个字符串的一个位置反转,要求操作后连续段长度仍不超过 \(2\) ,求使得两个字符串相等的最少操作次数。 \((1\le N\le5000)\)
题解:
为了表示连续段,考虑将每次 01 交替的地方记下来。
表示连续段可以记录交替处,方便看长度。
这样每次操作可以等价于把一个集合里的元素左移或右移一位或者在首尾新建一个元素。
并且可以保证相邻的元素颜色互不相同,元素之间也是不会交换位置的。
其实答案就是这些元素对应过去的距离差之和。
能够构造出一种方案满足条件(除非中间全都是两个两个加,说明两个串本来就相同)。
只需要枚举元素分别对应哪个,再求出最小值更新。时间复杂度: \(O(N^2)\)
但是还有一些麻烦的细节:
- 新建元素如何处理
需要再第一个集合首尾分别放第二个集合初始元素大小个 \(0\) 和 \(N\) 。
想一想发现是对的(挺妙啊)。
- 0 和 1 要对齐
如果只考虑集合的元素显然会出现 0 和 1 恰好反转的情况。
为了对齐,需要加一个开头是 1 集合内多个 \(0\) ,末尾是 1 集合内多个 \(N\) 。
这样两个集合集合都变成以 0 开头,以 0 结尾的了。
因此把两个集合填充成同样的大小,枚举前面有偶数个 0 的情况就能对齐了。
这题思路非常抽象,需要感性理解……
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=5005,inf=1e9;
int n,c1,c2,ans=inf;
vector<int> p,q,a,b;
char s[N],t[N];
void init(vector<int>&f,char*st){
if(st[1]=='1') f.push_back(0);
for(int i=1;i<n;i++)
if(st[i]!=st[i+1]) f.push_back(i);
if(st[n]=='1') f.push_back(n);
}
signed main(){
scanf("%d%s%s",&n,s+1,t+1);init(p,s);init(q,t);c1=p.size();c2=q.size();
for(int i=1;i<=c2;i++) a.push_back(0);
copy(p.begin(),p.end(),back_inserter(a));
for(int i=1;i<=c2;i++) a.push_back(n);
for(int i=0,sum;i<=c1+c2;i+=2){
b.clear();sum=0;
for(int j=1;j<=i;j++) b.push_back(0);
copy(q.begin(),q.end(),back_inserter(b));
for(int j=i+c2+1;j<=c1+c2+c2;j++) b.push_back(n);
for(int j=0;j<a.size();j++) sum+=abs(a[j]-b[j]);
ans=min(ans,sum);
}
printf("%d\n",ans);
return 0;
}