洛谷9.22

快速求和

题目背景

2023-10-08 update: 新增两组 hack。

2023-12-16 update: 新增两组 hack。

题目描述

给定一个数字字符串,用最小次数的加法让字符串等于一个给定的目标数字。每次加法就是在字符串的某个位置插入一个加号。在里面要的所有加号都插入后,就像做普通加法那样来求值。

例如,考虑字符串12,做 0 0 0 次加法,我们得到数字 12 12 12。如果插入 1 1 1 个加号,我们得到 3 3 3,因此,这个例子中,最少用 1 1 1 次加法就得到数字 3 3 3

再举一例,考虑字符串303和目标数字 6 6 6,最佳方法不是3+0+3。而是3+03。能这样做是因为一个数的前导 0 0 0 不会改变它的大小。

输入格式

第一行:一个字符串 s s s

第二行:一个整数 n n n

输出格式

一行一个整数表示最少的加法次数让 s s s 等于 n n n。如果怎么做都不能让 s s s 等于 n n n ,则输出 − 1 -1 1

样例 #1

样例输入 #1

99999
45

样例输出 #1

4

提示

数据规模与约定

对于 100 % 100\% 100% 的数据,保证 1 ≤ len ⁡ ( s ) ≤ 40 1\le \operatorname{len}(s)\le40 1len(s)40 1 ≤ n ≤ 1 0 5 1 \leq n\le10^5 1n105

分析

令dp[i][j]表示遍历到第i个数,总和为j的最小加号数目,预处理num[i][j]表示字符串i到j所代表的十进制数,两重循环遍历每个区间,令得到的数为tnum,则状态转移为 d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ k ] [ j − t n u m ] + 1 ) dp[i][j]=min(dp[i][j],dp[k][j-tnum]+1) dp[i][j]=min(dp[i][j],dp[k][jtnum]+1),其中i和k为遍历区间的端点

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 45, M = 1e5 + 5;
int dp[N][M];
int num[N][N];
int n;
int getNum(string s) {
    int res = 0;
    for(int i = 0; i < s.size(); i ++ ) {
        res = res * 10 + s[i] - '0';
    }
    return res;
}
void init(string s) {
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0] = 0;
    for(int i = 0; i < s.size(); i ++ ) {
        for(int j = i; j < s.size(); j ++ ) {
            string tmp = s.substr(i, j - i + 1);
            num[i + 1][j + 1] = getNum(tmp);
//            cout << i << " " << j << " " << num[i][j] << endl;
        }
    }
}
signed main() {
    string s;
    cin >> s;
    cin >> n;
    init(s);
    for(int i = 1; i <= s.size(); i ++ ) {
        for(int j = 0; j <= n; j ++ ) {
            for(int k = i - 1; k >= 0 && num[k + 1][i] <= j; k -- ) {
                dp[i][j] = min(dp[i][j], dp[k][j - num[k + 1][i]] + 1);
            }
        }
    }
    if(dp[s.size()][n] >= 45) cout << -1;
    else cout << dp[s.size()][n] - 1 << endl;
    return 0;
}



编辑距离

题目描述

A A A B B B 是两个字符串。我们要用最少的字符操作次数,将字符串 A A A 转换为字符串 B B B。这里所说的字符操作共有三种:

  1. 删除一个字符;
  2. 插入一个字符;
  3. 将一个字符改为另一个字符。

A , B A, B A,B 均只包含小写字母。

输入格式

第一行为字符串 A A A;第二行为字符串 B B B;字符串 A , B A, B A,B 的长度均小于 2000 2000 2000

输出格式

只有一个正整数,为最少字符操作次数。

样例 #1

样例输入 #1

sfdqxbw
gfdgw

样例输出 #1

4

提示

对于 100 % 100 \% 100% 的数据, 1 ≤ ∣ A ∣ , ∣ B ∣ ≤ 2000 1 \le |A|, |B| \le 2000 1A,B2000

分析

d p [ i ] [ j ] dp[i][j] dp[i][j] a a a的前 i i i个字符变成 b b b的前 j j j个字符所需要的最小操作,若 a [ i ] = = b [ j ] a[i]==b[j] a[i]==b[j],此时次数可以不变,即 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] dp[i][j]=dp[i-1][j-1] dp[i][j]=dp[i1][j1],也可以是 a a a添加一个字符,此时需要 a a a的前 i i i个字符和 b b b的前 j − 1 j-1 j1个字符相同,即 d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + 1 dp[i][j]=dp[i][j-1]+1 dp[i][j]=dp[i][j1]+1,也可以是a删除一个字符,此时需要a的前i-1个字符和b的前j个字符相同,即 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + 1 dp[i][j]=dp[i-1][j]+1 dp[i][j]=dp[i1][j]+1,若 a [ i ] ! = b [ j ] a[i]!=b[j] a[i]!=b[j],此时添加和删除的情况不变,还可以进行修改,因此这时候 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j]=dp[i-1][j-1]+1 dp[i][j]=dp[i1][j1]+1,最后这三种情况取最小即可,注意初始化, d p [ 0 ] [ i ] = i dp[0][i]=i dp[0][i]=i因为只能进行添加操作,同理 d p [ i ] [ 0 ] = i dp[i][0]=i dp[i][0]=i

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2005;
int dp[N][N];
int main() {
    string a, b;
    cin >> a >> b;
    int n = a.size(), m = b.size();
    for(int i = 0; i <= m; i ++ ) dp[0][i] = i;
    for(int i = 0; i <= n; i ++ ) dp[i][0] = i;
    for(int i = 1; i <= n; i ++ ) {
        for(int j = 1; j <= m; j ++ ) {
            if(a[i - 1] == b[j - 1]) dp[i][j] = min(min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1]);
            else dp[i][j] = min(min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 1);
        }
    }
    cout << dp[n][m] << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值