BZOJ 1082 SCOI 2005 栅栏 搜索+剪枝

见到最大最小化的先考虑二分答案。。那好就二分可以满足的需求个数。
首先可以确定的优化
1. 删掉比最小的需求都小的木板,删掉比最大给定木板还大的需求。
2. 优先满足更大的需求,从最小的木板开始切。
3. 如果有相等的需求,且上一个相等需求i切了j,那么该需求就从j开始切,可以避免重复搜索量。
4. 如果没被浪费的小于需求的总量就不合法。
概括地说就是要考虑初始的不合法情况,搜索的顺序/贪心的角度,重复搜索的情况,以及当前状态的合法性。

一过样例就交了。。。竟然1A好开心。
实测fread的读入优化并没有比直接getchar快。。
读入优化从8ms->4ms。
可惜只在第二页。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1001;
#define FOR(i,j,k) for(i=j;i<=k;i++)
int read() {  
    int s = 0, f = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    for (; '0' <= ch && ch <= '9'; ch = getchar()) s = s * 10 + ch - '0';
    return s * f;
}

int given[N], needs[N], given_num, needs_num, given_sum, needs_sum[N], waste, mid;

int dfs(int g, int n) {
    int i;
    if (!n) return 1;
    if (waste + needs_sum[mid] > given_sum) return 0; // 4

    // 2.2
    FOR(i,g,given_num) if (given[i] >= needs[n]) {
        given[i] -= needs[n];
        if (given[i] < needs[1]) waste += given[i];
        if (needs[n] == needs[n - 1]) { // 3
            if (dfs(i, n - 1)) g = -555;
        } else if (dfs(1, n - 1)) g = -555;
        if (given[i] < needs[1]) waste -= given[i];
        given[i] += needs[n];
        if (g == -555) return 1;
    }
    return 0;
}

int main() {
    int tot = 0, i, l, r, ans = 0;

    given_num = read();
    FOR(i,1,given_num) given[i] = read();
    sort(given + 1, given + given_num + 1);

    needs_num = read();
    FOR(i,1,needs_num) needs[i] = read();
    sort(needs + 1, needs + needs_num + 1);

    // 1.2
    while (given[given_num] < needs[needs_num]) --needs_num;
    // 1.1
    FOR(i,1,given_num)
        if (given[i] >= needs[1]) given[++tot] = given[i];
        else waste += given[i]; // 4
    given_num = tot; needs_sum[0] = 0; given_sum = 0;

    FOR(i,1,given_num) given_sum += given[i];
    FOR(i,1,needs_num) needs_sum[i] = needs_sum[i - 1] + needs[i];

    l = 1; r = needs_num;
    while (l <= r) {
        mid = l + r >> 1;
        // 2.1
        if (!dfs(1, mid)) r = mid - 1;
        else l = mid + 1, ans = mid;
    }
    printf("%d", ans);

    return 0;
}

1082: [SCOI2005]栅栏

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1351 Solved: 592

Description

农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材。于是农夫约翰到木材店购买木材。可是木材店老板说他这里只剩下少部分大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为10的木板可以切成长度为8和2的两个木板。你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰最多能够得到多少他所需要的木板。

Input

第一行为整数m(m<= 50)表示木材店老板可以提供多少块木材给约翰。紧跟着m行为老板提供的每一块木板的长度。接下来一行(即第m+2行)为整数n(n <= 1000),表示约翰需要多少木材。接下来n行表示他所需要的每一块木板的长度。木材的规格小于32767。(对于店老板提供的和约翰需要的每块木板,你只能使用一次)。

Output

只有一行,为约翰最多能够得到的符合条件的木板的个数。

Sample Input

4
25 30 40 50
10
15 16 17 18 19 20 21 24 25 30

Sample Output

7

HINT

25切出 21 30切出 20 40切出 19、18 50切出 15、16、17

你可能感兴趣的:(搜索,剪枝,bzoj,SCOI,省选)