update 666 T634195咋这么难呢
This commit is contained in:
parent
e2b339fa8e
commit
330aed68f6
@ -1,121 +1,122 @@
|
||||
// #include <cstdint>
|
||||
// #include <iostream>
|
||||
// #include <string>
|
||||
|
||||
// using ll = int64_t;
|
||||
// ll n,ans{};
|
||||
// std::string s1,s2;
|
||||
|
||||
// bool isBig(char c){
|
||||
// return 'A'<=c&&c<='Z';
|
||||
// }
|
||||
|
||||
// void swapBS(char &c){
|
||||
// if(isBig(c)){
|
||||
// c=c-'A'+'a';
|
||||
// }else{
|
||||
// c=c-'a'+'A';
|
||||
// }
|
||||
// }
|
||||
|
||||
// ll rn(ll idx){
|
||||
// ll ans{};
|
||||
// if(s2[idx]>=s1[idx]){
|
||||
// ans=s2[idx]-s1[idx];
|
||||
// }else{
|
||||
|
||||
// }
|
||||
// // return
|
||||
// }
|
||||
|
||||
// int main(){
|
||||
// std::cin>>n;
|
||||
// std::cin>>s1>>s2;
|
||||
// s1=' '+s1;
|
||||
// s2=' '+s2;
|
||||
|
||||
// for(ll i=s1.size()-1;i>=1;i--){
|
||||
// if(isBig(s1[i])!=isBig(s2[i]))ans++;
|
||||
// swapBS(s1[i]);
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
// /*
|
||||
// 1. 改变大小写
|
||||
// 2. 前后移动字典序
|
||||
// 1. 如果向右越界那么前面一个字母的字典序+1
|
||||
// 2. 向左则反之
|
||||
// 3. 第一位不用考虑这些
|
||||
// 4. 类似于26进制
|
||||
// */
|
||||
|
||||
#include <cstdint>
|
||||
#include<iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
using namespace std;
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
using ll = int64_t;
|
||||
|
||||
int main() {
|
||||
ios::sync_with_stdio(false);
|
||||
cin.tie(0);
|
||||
ll n;
|
||||
std::string s1,s2;
|
||||
std::vector<std::vector<std::pair<ll, ll>>> dp;
|
||||
/*
|
||||
dp[k][i]=tuple(ll w,ll jt)
|
||||
k从右往左第k个字符
|
||||
i减少增加
|
||||
w消耗的次数
|
||||
jt 0不变,1进位,-1退位
|
||||
*/
|
||||
|
||||
int n;
|
||||
cin >> n;
|
||||
string s1, s2;
|
||||
cin >> s1 >> s2;
|
||||
|
||||
ll co = 0;
|
||||
vector<int> A(n), B(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (isupper(s1[i])) {
|
||||
A[i] = s1[i] - 'A';
|
||||
} else {
|
||||
A[i] = s1[i] - 'a';
|
||||
}
|
||||
if (isupper(s2[i])) {
|
||||
B[i] = s2[i] - 'A';
|
||||
} else {
|
||||
B[i] = s2[i] - 'a';
|
||||
}
|
||||
bool s1_upper = isupper(s1[i]);
|
||||
bool s2_upper = isupper(s2[i]);
|
||||
if (s1_upper != s2_upper) {
|
||||
co++;
|
||||
std::tuple<ll,ll> rw(ll i){
|
||||
ll ans{};
|
||||
ll jt{};
|
||||
char c = s1[i];
|
||||
while(c!=s2[i]){
|
||||
ans++;
|
||||
if(c=='z'){
|
||||
jt=1;
|
||||
c='a';
|
||||
continue;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
|
||||
vector<ll> diff(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
diff[i] = B[i] - A[i];
|
||||
return {ans,jt};
|
||||
}
|
||||
std::tuple<ll,ll> lw(ll i){
|
||||
ll ans{};
|
||||
ll jt{};
|
||||
char c = s1[i];
|
||||
while(c!=s2[i]){
|
||||
ans++;
|
||||
if(c=='a'){
|
||||
jt=-1;
|
||||
c='z';
|
||||
continue;
|
||||
}
|
||||
c--;
|
||||
}
|
||||
return {ans,jt};
|
||||
}
|
||||
|
||||
for (int i = n - 1; i >= 1; i--) {
|
||||
ll x = diff[i];
|
||||
ll temp = x + 12;
|
||||
ll q;
|
||||
if (temp >= 0) {
|
||||
q = temp / 26;
|
||||
} else {
|
||||
q = temp / 26;
|
||||
if (temp % 26 != 0) {
|
||||
q--;
|
||||
ll setr(char &c){
|
||||
ll jt{};
|
||||
if(c=='z'){
|
||||
c='a';
|
||||
jt=1;
|
||||
}else{
|
||||
c=c+1;
|
||||
}
|
||||
return jt;
|
||||
}
|
||||
|
||||
ll setl(char &c){
|
||||
ll jt{};
|
||||
if(c=='a'){
|
||||
jt=-1;
|
||||
c='z';
|
||||
}else{
|
||||
c=c-1;
|
||||
}
|
||||
return jt;
|
||||
}
|
||||
|
||||
int main(){
|
||||
std::cin>>n>>s1>>s2;
|
||||
|
||||
s1=' '+s1;
|
||||
s2=' '+s2;
|
||||
dp.resize(s1.size()+1,std::vector<std::pair<ll, ll>>(2,std::pair<ll, ll>(std::numeric_limits<ll>::max(),0)));
|
||||
dp[s1.size()][0].first = dp[s1.size()][1].first = 0;
|
||||
for(ll k=s1.size()-1;k>=1;k--){
|
||||
ll nw{};
|
||||
if(isupper(s1[k]) != isupper(s2[k])){
|
||||
nw++;
|
||||
}
|
||||
s1[k]=tolower(s1[k]);
|
||||
s2[k]=tolower(s2[k]);
|
||||
for(ll i=0;i<(ll)dp[k+1].size();i++){
|
||||
if(dp[k+1][i].second==1){
|
||||
setr(s1[k]);
|
||||
auto[tlw,tljt] = lw(k);
|
||||
auto[trw,trjt]=rw(k);
|
||||
dp[k][0].first=std::min(dp[k][0].first,tlw+dp[k+1][i].first+nw);
|
||||
if(dp[k][0].first==tlw+dp[k+1][i].first+nw)dp[k][0].second=tljt;
|
||||
dp[k][1].first=std::min(dp[k][1].first,trw+dp[k+1][i].first+nw);
|
||||
if(dp[k][1].first==trw+dp[k+1][i].first+nw)dp[k][1].second=trjt;
|
||||
setl(s1[k]);
|
||||
}else if(dp[k+1][i].second==-1){
|
||||
setl(s1[k]);
|
||||
auto[tlw,tljt] = lw(k);
|
||||
auto[trw,trjt]=rw(k);
|
||||
dp[k][0].first=std::min(dp[k][0].first,tlw+dp[k+1][i].first+nw);
|
||||
if(dp[k][0].first==tlw+dp[k+1][i].first+nw)dp[k][0].second=tljt;
|
||||
dp[k][1].first=std::min(dp[k][1].first,trw+dp[k+1][i].first+nw);
|
||||
if(dp[k][1].first==trw+dp[k+1][i].first+nw)dp[k][1].second=trjt;
|
||||
setr(s1[k]);
|
||||
}else{
|
||||
auto[tlw,tljt] = lw(k);
|
||||
auto[trw,trjt]=rw(k);
|
||||
dp[k][0].first=std::min(dp[k][0].first,tlw+dp[k+1][i].first+nw);
|
||||
if(dp[k][0].first==tlw+dp[k+1][i].first+nw)dp[k][0].second=tljt;
|
||||
dp[k][1].first=std::min(dp[k][1].first,trw+dp[k+1][i].first+nw);
|
||||
if(dp[k][1].first==trw+dp[k+1][i].first+nw)dp[k][1].second=trjt;
|
||||
}
|
||||
}
|
||||
ll r = x - 26 * q;
|
||||
diff[i] = r;
|
||||
diff[i - 1] += q;
|
||||
}
|
||||
|
||||
ll mo = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
mo += abs(diff[i]);
|
||||
}
|
||||
|
||||
ll ans = mo + co;
|
||||
cout << ans << endl;
|
||||
|
||||
return 0;
|
||||
std::cout<<std::min(dp[1][0].first,dp[1][1].first)<<'\n';
|
||||
}
|
@ -1,104 +1,138 @@
|
||||
为了解决这个问题,我们需要将上排字符串通过最少的操作转换为下排字符串。操作包括改变字母的大小写或按字典序向前/向后移动字母,其中移动字母可能引发进位或退位传播。我们的目标是计算最小操作次数。
|
||||
### 问题分析
|
||||
潘多拉魔盒问题要求通过最少的魔法操作使上排字母序列与下排字母序列相同。魔法操作包括:
|
||||
1. **改变字母大小写**:成本为1,仅影响当前字母的大小写。
|
||||
2. **改变字母值(向前或向后移动一位)**:成本为1,但若移动导致字母从 'z'/'Z' 变为 'a'/'A' 或反之,则触发进位/退位,向左传播并影响左侧字母的值(大小写不变)。
|
||||
|
||||
### 方法思路
|
||||
1. **问题分析**:
|
||||
- **大小写操作**:独立于移动操作,每个位置的大小写转换只需1次操作,且不传播。
|
||||
- **字母移动操作**:每次移动一个字母可能引发向左的进位或退位传播,相当于在26进制数上进行加减操作。每个位置的净改变量可以通过调整系数来最小化总操作次数。
|
||||
目标是最小化总操作次数。解决方案分为两部分:
|
||||
- **大小写匹配**:如果某位置的大小写不匹配,必须使用一次大小写翻转操作(成本1)。
|
||||
- **字母值匹配**:通过值移位操作(可能触发进位/退位)使字母值匹配。值移位操作的最小成本通过动态规划计算,考虑进位传播的影响。
|
||||
|
||||
2. **关键思路**:
|
||||
- **字母值转换**:将每个字符映射到0-25的整数(忽略大小写)。
|
||||
- **大小写操作计数**:统计初始和目标字符串大小写不同的位置数。
|
||||
- **差分数组**:计算目标字母值减去初始字母值的差分数组。
|
||||
- **进位调整**:从右向左调整差分数组,使每个位置的改变量落在[-12, 13]区间内,以最小化操作次数。调整过程中,进位或退位会向左传播。
|
||||
- **总操作数**:移动操作数为调整后差分数组各元素绝对值之和,加上大小写操作数。
|
||||
### 解决思路
|
||||
1. **大小写成本计算**:
|
||||
- 遍历每个位置,如果上下排字母大小写不同,则成本增加1。
|
||||
|
||||
### 解决代码
|
||||
2. **字母值成本计算(动态规划)**:
|
||||
- **状态定义**:从右向左处理序列(索引从高到低),对于每个位置 `i`,计算当前值 `x = val_S[i] + carry_in`,其中 `carry_in` 是来自右侧的进位(初始为0)。
|
||||
- **状态转移**:对于每个位置 `i`,选择输出进位 `carry_out`(-1、0 或 1),使 `m = val_T[i] + 26 * carry_out - x` 的绝对值最小。`|m|` 是位置 `i` 的值操作成本,`carry_out` 传递到左侧。
|
||||
- **进位处理**:`carry_out` 的选择基于最小化 `|m|`,考虑进位对左侧的影响。
|
||||
- **总成本**:所有位置的 `|m|` 之和即为值操作总成本。
|
||||
|
||||
3. **总成本**:大小写成本 + 值操作成本。
|
||||
|
||||
### 算法步骤
|
||||
1. **解析输入**:读取序列长度 `n` 和上下排字符串 `S` 和 `T`。
|
||||
2. **预处理**:将每个字符分解为值(0-25)和大小写(0表示小写,1表示大写)。
|
||||
3. **计算大小写成本**:遍历每个位置,若大小写不同,成本加1。
|
||||
4. **计算值操作成本**:
|
||||
- 初始化进位 `carry_in = 0` 和值成本 `value_cost = 0`。
|
||||
- 从右向左遍历位置(`i = n-1` 到 `0`):
|
||||
- 计算当前值 `x = val_S[i] + carry_in`。
|
||||
- 对每个 `carry_out` 候选值(-1, 0, 1):
|
||||
- 计算 `m = val_T[i] + 26 * carry_out - x`。
|
||||
- 选择使 `|m|` 最小的 `carry_out`。
|
||||
- 累加最小 `|m|` 到 `value_cost`,更新 `carry_in = best_carry_out`。
|
||||
5. **输出总成本**:`case_cost + value_cost`.
|
||||
|
||||
### 复杂度分析
|
||||
- **时间复杂度**:O(n),遍历序列一次,每个位置处理常数个候选进位。
|
||||
- **空间复杂度**:O(n),存储值序列和大小写信息。
|
||||
|
||||
### 代码实现(C++)
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
ios::sync_with_stdio(false);
|
||||
cin.tie(0);
|
||||
|
||||
int n;
|
||||
cin >> n;
|
||||
string s1, s2;
|
||||
cin >> s1 >> s2;
|
||||
string s_top, s_bottom;
|
||||
cin >> s_top >> s_bottom;
|
||||
|
||||
long long case_ops = 0;
|
||||
vector<int> A(n), B(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (isupper(s1[i])) {
|
||||
A[i] = s1[i] - 'A';
|
||||
vector<int> val_S(n), val_T(n);
|
||||
vector<int> case_S(n), case_T(n);
|
||||
|
||||
// 预处理:提取每个字符的值和大小写
|
||||
for (int i = 0; i < n; ++i) {
|
||||
char c = s_top[i];
|
||||
if (islower(c)) {
|
||||
val_S[i] = c - 'a';
|
||||
case_S[i] = 0;
|
||||
} else {
|
||||
A[i] = s1[i] - 'a';
|
||||
}
|
||||
if (isupper(s2[i])) {
|
||||
B[i] = s2[i] - 'A';
|
||||
} else {
|
||||
B[i] = s2[i] - 'a';
|
||||
}
|
||||
bool s1_upper = isupper(s1[i]);
|
||||
bool s2_upper = isupper(s2[i]);
|
||||
if (s1_upper != s2_upper) {
|
||||
case_ops++;
|
||||
val_S[i] = c - 'A';
|
||||
case_S[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
vector<long long> diff(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
diff[i] = B[i] - A[i];
|
||||
for (int i = 0; i < n; ++i) {
|
||||
char c = s_bottom[i];
|
||||
if (islower(c)) {
|
||||
val_T[i] = c - 'a';
|
||||
case_T[i] = 0;
|
||||
} else {
|
||||
val_T[i] = c - 'A';
|
||||
case_T[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = n - 1; i >= 1; i--) {
|
||||
long long x = diff[i];
|
||||
long long temp = x + 12;
|
||||
long long q;
|
||||
if (temp >= 0) {
|
||||
q = temp / 26;
|
||||
} else {
|
||||
q = temp / 26;
|
||||
if (temp % 26 != 0) {
|
||||
q--;
|
||||
// 计算大小写成本
|
||||
int case_cost = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (case_S[i] != case_T[i]) {
|
||||
case_cost++;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算值操作成本(从右向左动态规划)
|
||||
int value_cost = 0;
|
||||
int carry_in = 0; // 初始进位为0
|
||||
|
||||
for (int i = n - 1; i >= 0; --i) {
|
||||
int x = val_S[i] + carry_in;
|
||||
int best_cost = 1e9;
|
||||
int best_carry_out = 0;
|
||||
|
||||
// 尝试三种进位输出: -1, 0, 1
|
||||
for (int cand_carry_out : {-1, 0, 1}) {
|
||||
int m = val_T[i] + 26 * cand_carry_out - x;
|
||||
int abs_m = abs(m);
|
||||
if (abs_m < best_cost) {
|
||||
best_cost = abs_m;
|
||||
best_carry_out = cand_carry_out;
|
||||
}
|
||||
}
|
||||
long long r = x - 26 * q;
|
||||
diff[i] = r;
|
||||
diff[i - 1] += q;
|
||||
|
||||
value_cost += best_cost;
|
||||
carry_in = best_carry_out; // 传递进位到左侧
|
||||
}
|
||||
|
||||
long long move_ops = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
move_ops += abs(diff[i]);
|
||||
}
|
||||
|
||||
long long ans = move_ops + case_ops;
|
||||
cout << ans << endl;
|
||||
int total_cost = case_cost + value_cost;
|
||||
cout << total_cost << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 代码解释
|
||||
1. **输入处理**:
|
||||
- 读取字符串长度 `n` 和上下排字符串 `s1`、`s2`。
|
||||
|
||||
2. **大小写操作计数**:
|
||||
- 遍历每个字符,将字母转换为0-25的整数值(忽略大小写),并记录大小写是否相同。若不同,则增加 `case_ops`。
|
||||
### 示例解释
|
||||
输入示例:
|
||||
```
|
||||
5
|
||||
AbCDx
|
||||
dECba
|
||||
```
|
||||
- **大小写成本**:位置0(A vs d)、1(b vs E)、3(D vs b)大小写不同,成本为3。
|
||||
- **值操作成本计算**:
|
||||
- 值序列:S = [0,1,2,3,23], T = [3,4,2,1,0]。
|
||||
- 从右向左处理:
|
||||
- i=4: x=23, T[4]=0 → min |m|=3 (carry_out=1)。
|
||||
- i=3: x=3+1=4, T[3]=1 → min |m|=3 (carry_out=0)。
|
||||
- i=2: x=2, T[2]=2 → min |m|=0 (carry_out=0)。
|
||||
- i=1: x=1, T[1]=4 → min |m|=3 (carry_out=0)。
|
||||
- i=0: x=0, T[0]=3 → min |m|=3 (carry_out=0)。
|
||||
- 值成本总和:3+3+0+3+3=12。
|
||||
- **总成本**:3(大小写) + 12(值操作) = 15,与输出一致。
|
||||
|
||||
3. **差分数组初始化**:
|
||||
- 计算目标字母值与初始字母值的差值,存入 `diff` 数组。
|
||||
|
||||
4. **进位调整**:
|
||||
- 从右向左(低位到高位)调整 `diff` 数组。对每个位置计算调整值 `q` 和余数 `r`,确保 `r` 在[-12, 13]区间内,并将进位 `q` 传递到左侧位置。
|
||||
|
||||
5. **移动操作计数**:
|
||||
- 调整后,`diff` 数组各元素的绝对值之和即为移动操作次数 `move_ops`。
|
||||
|
||||
6. **输出结果**:
|
||||
- 总操作数为 `move_ops` 和 `case_ops` 之和。
|
||||
|
||||
这种方法高效地处理了字母移动引发的进位传播,并通过调整差分数组最小化操作次数,结合独立的大小写转换操作,得到最优解。
|
||||
此算法高效处理了进位传播和大小写匹配,确保最小操作次数。
|
Loading…
Reference in New Issue
Block a user