【题解】P1874 快速求和 二维背包
发一篇类似于背包的二维DP吧
首先设状态:
我们设 f [ i ][ j ] 表示 前 i位数字 组成 j 的最少加法次数
所以,类似于背包的转移方式,我们可以得到如下方程:
f [ i ] [ j ] = min { f [ i - k ] [ j - s [ i - k + 1 ] [ i ] ] + 1 , f [ i ] [ j ] } ;
即第二维类比于背包中的体积,
其中,s [ ] [ ] 数组是预处理出来的“原数” , 即不添加号时的“非一位数”多位数 , 由于数据中 n <= 100000 , 所以预处理出6位就足够了,但是要防备 像“03”的情况,所以再多开几位就好了………………
惨痛的教训,我调了好几遍
方程出来后,然后是初始化 : 因为求的是 min 值 ,所以全部初始化为极大值, 但是要切记 f [ 0 ] [ 0 ] = 0 , 否则无法完成转移
简洁的 DP 清晰的思路 代码如下 :
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#define ll unsigned long long
#define E 100007
using namespace std ;
string sr ;
ll a[E] , s[43][43] , f[43][E] ; // 前 i 位数字 组成 j 的最少加法次数 f[i][j] = min { f[i-k][j-s[i-k+1][i]] + 1 , f[i][j] } ;
int main(){
cin >> sr ;
ll len = sr.length() ;
for(ll i = 0 ; i < len ; i ++) a[i + 1] = sr[i] - '0' ;
for(ll i = 1 ; i <= len ; i ++)
{
s[i][i] = a[i] ;
for(ll j = i ; j - i <= 11 && j <= len ; j ++)
s[i][j] = s[i][j-1] * 10 + a[j] ;
}
ll n ; cin >> n ;
for(ll i = 0 ; i <= len + 1 ; i ++) for(ll j = 0 ; j <= n + 1 ; j ++) f[i][j] = 0x7fffffff ; f[0][0] = 0 ;
for(ll i = 1 ; i <= len ; i ++)
for(ll k = 1 ; k <= 11 && i + k <= len + 3 ; k ++)
if(i >= k)
for(ll j = s[i-k+1][i] ; j <= n ; j ++)
f[i][j] = min(f[i-k][j-s[i-k+1][i]] + 1 , f[i][j]) ;
if(n == 0) cout << 0 << endl ;
else if(f[len][n] > len ) cout << -1 << endl ;
else cout << f[len][n] - 1 << endl ;
return -0 ;
}
@作者troubler