题目链接: 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 }