首页 > 代码库 > cf779D(记忆化dp)

cf779D(记忆化dp)

题目链接: http://codeforces.com/problemset/problem/799/D

 

题意: 给出两个矩阵边长 a, b, 和 w, h, 以及一个 c 数组, 可选择 c 数组中任意数字乘上w 或 h. 数组中每个数字最多只能用一次. 求最少选择多少个数字可使得边长为 a, b 的矩阵能放到变化后的矩阵中.

 

思路: log2(1e5) = 17, 即最多需要对一条边乘17个数字, 要是完全暴力的话需要 2^34 的时间复杂度, 显然不行.

本题 dp 可解, 先给 c 降序排列, 用 dp[i][j] = k 表示到第 i 个元素, 其中一条边为 j 时另外一条边最大为 k.

那么动态转移方程式为: dp[i][j] = max(dp[i - 1][j / c[i]], dp[i - 1][j] * c[i])

但是用普通 dp 的话会有两个问题, 其一是 j / c[i] 整除问题, 这个可以先判断一下能否整除解决. 其二是 w * c[i] 非常大, 如:

100000 100000 99999 99999 2
30000 30000

设定  0 < j <= 1e5 显然不够, 要是 j 太大则会 tle. 虽然不一定没有解决的方法, 但是显然可以不用这么麻烦, 将其改成记忆化dp即可.

 

代码:

技术分享
 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <algorithm>
 5 #define ll long long
 6 using namespace std;
 7 
 8 const int MAXN = 4e5 + 10;
 9 ll c[MAXN], dp[40][MAXN];
10 int n, a, b;
11 
12 bool cmp(ll x, ll y){
13     return x > y;
14 }
15 
16 int dfs(int indx, ll w, ll h){
17     if(w >= a && h >= b || w >= b && h >= a) return indx;
18     if(indx > 34 || indx >= n) return MAXN;
19     if(w >= 1e5) w = 1e5; //注意越界
20     if(h >= 1e5) h = 1e5;
21     if(dp[indx][w] != -1) return dp[indx][w];
22     return dp[indx][w] = min(dfs(indx + 1, w * c[indx], h), dfs(indx + 1, w, h * c[indx]));
23 }
24 
25 int main(void){
26     int h, w;
27     scanf("%d%d%d%d%d", &a, &b, &h, &w, &n);
28     for(int i = 0; i < n; i++){
29         scanf("%lld", &c[i]);
30     }
31     sort(c, c + n, cmp);
32     memset(dp, -1, sizeof(dp));
33     int sol = dfs(0, w, h);
34     if(sol != MAXN) cout << sol << endl;
35     else cout << -1 << endl;
36     return 0;
37 }
View Code

 

cf779D(记忆化dp)