Codeforces 799D Field expansion - 搜索 - 贪心

In one of the games Arkady is fond of the game process happens on a rectangular field. In the game process Arkady can buy extensions for his field, each extension enlarges one of the field sizes in a particular number of times. Formally, there are n extensions, the i-th of them multiplies the width or the length (by Arkady's choice) by ai. Each extension can't be used more than once, the extensions can be used in any order.

Now Arkady's field has size h × w. He wants to enlarge it so that it is possible to place a rectangle of size a × b on it (along the width or along the length, with sides parallel to the field sides). Find the minimum number of extensions needed to reach Arkady's goal.

Input

The first line contains five integers a, b, h, w and n (1 ≤ a, b, h, w, n ≤ 100 000) — the sizes of the rectangle needed to be placed, the initial sizes of the field and the number of available extensions.

The second line contains n integers a1, a2, ..., an (2 ≤ ai ≤ 100 000), where ai equals the integer a side multiplies by when the i-th extension is applied.

Output

Print the minimum number of extensions needed to reach Arkady's goal. If it is not possible to place the rectangle on the field with all extensions, print -1. If the rectangle can be placed on the initial field, print 0.

Examples
Input
3 3 2 4 4
2 5 4 10
Output
1
Input
3 3 3 3 5
2 3 5 4 2
Output
0
Input
5 5 1 2 3
2 2 3
Output
-1
Input
3 4 1 1 3
2 3 2
Output
3
Note

In the first example it is enough to use any of the extensions available. For example, we can enlarge h in 5 times using the second extension. Then h becomes equal 10 and it is now possible to place the rectangle on the field.


  题目大意 给定一个w × h的矩形,每次可以将w或h乘上ai(ai不能重复使用,并且与顺序无关)。问至少扩充多少次可以使得这个矩形中包含一个a × b的子矩形,如果无解输出-1。

  因为这次比赛D题把题意理解错了,导致没有蓝,所以。。。决定把以往比赛没有A的D题拉出A掉。

  因为w和h呈指数级增长,所以显然爆搜。

  然后根据数学的直觉和人生的哲理,你可以知道题目要求最少的,所以先用大的ai更划算。因此可以先将ai排一次序。

  既然是求最少,又确定是搜索,所以显然bfs。加上判重。好了这道题AC了。(网上那群说这样直接爆搜O(234)是不是看了某一个人的题解或者用dfs或者bfs没判重啊?)

  (然而我这道题第一次写bfs是忘判重,完美MLE + TLE)

  接着来解释一下,为什么加上判重后不会TLE,首先正确的bfs的时间复杂度是O(状态总数 * 转移时间)(如果判重是个常数的话)。这里的转移时间复杂度可以当O(1)处理。考虑最坏的情况,每个ai都不同,那就是阶乘级增长,对于w来说,不同的取值不会超过29(9! = 362880,考虑从1 ~ 9任取一个子集,再把这中间的元素乘起来,再乘w的初值得到w的一个取值,但是显然 2 * 4 = 8(意思是说这中间还会出现重复的取值),所以,不同取值的个数绝不会超过29),对于h同理,所以总状态数不会超过218。由于不能直接开vis数组,所以这里用的set来判重,多了一个log,但是足以通过这道题。

Code

 1 /**
 2  * Codeforces
 3  * Problem#799D
 4  * Accepted
 5  * Time: 31ms
 6  * Memory: 2500k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 int a, b, c, d, n;
13 int* arr;
14 
15 typedef class Data {
16     public:
17         int nc;
18         int nd;
19         int step;
20         
21         Data():nc(0), nd(0), step(0) {        }
22         Data(long long nc, long long nd, int step):step(step) {
23             this->nc = min(nc, (long long)a);
24             this->nd = min(nd, (long long)b);
25         }
26         
27         boolean isfin() {
28             return nc >= a && nd >= b;
29         }
30         
31         boolean operator < (Data b) const {
32             if(nc != b.nc)    return nc < b.nc;
33             return nd < b.nd;
34         }
35 }Data;
36 
37 inline void init() {
38     scanf("%d%d%d%d%d", &a, &b, &c, &d, &n);
39     arr = new int[(n + 1)];
40     for(int i = 1; i <= n; i++) {
41         scanf("%d", arr + i);
42     }
43 }
44 
45 set<Data> se; 
46 queue<Data> que;
47 inline int bfs(int c, int d) {
48     Data s(c, d, 0);
49     if(s.isfin())    return 0;
50     while(!que.empty())    que.pop();
51     que.push(s);
52     while(!que.empty()) {
53         Data e = que.front();
54         que.pop();
55 //        printf("e:%d %d %d\n", e.nc, e.nd, e.step);
56         
57         if(e.nc < a) {
58             Data eu(e.nc * 1LL * arr[e.step + 1], e.nd, e.step + 1);
59 //            printf("%d %d %d\n", eu.nc, eu.nd, eu.step);
60             if(eu.isfin())    return eu.step;
61             if(eu.step < n && !se.count(eu))
62                 que.push(eu), se.insert(eu);
63         }
64         
65         if(e.nd < b) {
66             Data eu(e.nc, e.nd * 1LL * arr[e.step + 1], e.step + 1);
67 //            printf("%d %d %d\n", eu.nc, eu.nd, eu.step);
68             if(eu.isfin())    return eu.step;
69             if(eu.step < n && !se.count(eu))
70                 que.push(eu), se.insert(eu);
71         }
72     }
73     return -1;
74 }
75 
76 inline void solve() {
77     sort(arr + 1, arr + n + 1, greater<int>());
78     while(n && arr[n] == 1)    n--;
79     int res1 = bfs(c, d), res2 = bfs(d, c);
80     if(res1 == -1 && res2 == -1)    puts("-1");
81     else if(res1 == -1 || res2 == -1)    printf("%d\n", max(res1, res2));
82     else printf("%d\n", min(res1, res2));
83 }
84 
85 int main() {
86     init();
87     solve();
88     return 0;
89 }
Field expansion(bfs)

  既然可以bfs,那我就试试A*加速,然而。。为什么cf和codevs一样有毒,这两个OJ上跑A*和我家机子上跑出来的结果不一样?有毒吧。。。

  但是还是佩服网上那些人的智商。。竟然想出来了一个用dfs + 鬼畜的优化跑过去的做法(给我500年我都想不出来)。

  当ai大于2时,,dfs不会出任何事情。当ai等于2时,显然有贡献的数就只有2了(乘了一个1和没有乘有区别吗),然后就没有必要搜下去,就可直接while计算出还需要的扩充的次数。

Code

 1 /**
 2  * Codeforces
 3  * Problem#799D
 4  * Accepted
 5  * Time: 15ms
 6  * Memory: 2428k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 #define smin(a, b) a = min(a, b)
11 typedef bool boolean;
12 const signed int inf = 1e9;
13 
14 int a, b, c, d, n;
15 int* arr;
16 
17 inline void init() {
18     scanf("%d%d%d%d%d", &a, &b, &c, &d, &n);
19     arr = new int[(n + 1)];
20     for(int i = 1; i <= n; i++) {
21         scanf("%d", arr + i);
22     }
23 }
24 
25 int res = 35;
26 int deplimit = 20;
27 void dfs(int dep, int nc, int nd) {
28 //    printf("%d %d %d\n", dep, nc, nd);
29     if(nc >= a && nd >= b) {
30         deplimit = dep, smin(res, dep);
31 //        cout << dep << endl;
32         return;
33     }
34     if(dep == n)    return;
35     if(arr[dep + 1] == 2) {
36         deplimit = dep;
37         while(nc < a && dep < n)    nc <<= 1, dep++;
38         while(nd < b && dep < n)    nd <<= 1, dep++;
39         if(nc < a || nd < b)    return;
40 //        cout << dep << endl;
41         smin(res, dep);
42     }
43     if(dep == deplimit)    return;
44     long long nnc = nc * 1LL * arr[dep + 1], nnd = nd * 1LL * arr[dep + 1];
45     if(nc < a)    dfs(dep + 1, smin(nnc, (long long)a), nd);
46     if(nd < b)    dfs(dep + 1, nc, smin(nnd, (long long)b));
47 }
48 
49 inline void solve() {
50     sort(arr + 1, arr + n + 1, greater<int>());
51     while(n && arr[n] == 1)    n--;
52     dfs(0, c, d), dfs(0, d, c);
53     printf("%d\n", (res < 35) ? (res) : (-1));
54 }
55 
56 int main() {
57     init();
58     solve();
59     return 0;
60 }

转载于:https://www.cnblogs.com/yyf0309/p/7274284.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值