USACO: Fence Rails

    技术2022-05-20  64

    Fence Rails Burch, Kolstad, and Schrijvers

    Farmer John is trying to erect a fence around part of his field. He has decided on the shape of the fence and has even already installed the posts, but he's having a problem with the rails. The local lumber store has dropped off boards of varying lengths; Farmer John must create as many of the rails he needs from the supplied boards.

    Of course, Farmer John can cut the boards, so a 9 foot board can be cut into a 5 foot rail and a 4 foot rail (or three 3 foot rails, etc.). Farmer John has an `ideal saw', so ignore the `kerf' (distance lost during sawing); presume that perfect cuts can be made.

    The lengths required for the rails might or might not include duplicates (e.g., a three foot rail and also another three foot rail might both be required). There is no need to manufacture more rails (or more of any kind of rail) than called for the list of required rails.

    PROGRAM NAME: fence8

    INPUT FORMAT

    Line 1: N (1 <= N <= 50), the number of boards Line 2..N+1: N lines, each containing a single integer that represents the length of one supplied board Line N+2: R (1 <= R <= 1023), the number of rails Line N+3..N+R+1: R lines, each containing a single integer (1 <= ri <= 128) that represents the length of a single required fence rail

    SAMPLE INPUT (file fence8.in)

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

    OUTPUT FORMAT

    A single integer on a line that is the total number of fence rails that can be cut from the supplied boards. Of course, it might not be possible to cut all the possible rails from the given boards.

    SAMPLE OUTPUT (file fence8.out)

    7

    HINTS (use them carefully!)

           HINT 1  

     

    解题思路:

    此题用到的主要算法是迭代深搜,所谓迭代深搜的概念其实我也不能完全把握,只能把这个算法描述为“类似广搜和深搜的一种结合体”,具体就体现在函数中对自己的递归调用是放在一个for循环里面的,而后面的搜索要不要执行是由前面搜索出来的结果决定的。

    这种算法的适用性可以用这个题目来记住:搜索深度无法确定,但是按照某一个特定的顺序搜索,搜出的第一个结果就是要求的最优解。

     

    这个题目的做法就是,把要切成的fails长度升序排列,原材料boards降序排列,然后考察第0~i个fails是否可以从这些boards中切出来,这个地方就用到了迭代深搜。

    注意搜索中保留和还原boards的状态非常重要:假如当前的裁切方案可行,在board被裁切后的状态会被保留下来,提供给更深一层的搜索使用,而一旦下层搜索找不到任何可行方案,则board会被还原,不会影响到下一步搜索的正确性

     

    最后,写这个程序我参考了不少别人的代码,但是多是高手写的比较晦涩难懂,为了我以后复习用我还是认认真真写了一个注释版的,同时把可能想到的各种优化都用上了,选排,桶排,二分搜索,ID_DFS本来只需要一个参数,但是考虑到fails势必存在很多长度一样的,我又加了一个参数lwIdx来表示从哪一个boards开始搜索裁切方案,在多个fails重复的情况下可以避免一些重复判断,还有很多细微的剪枝技巧,比如不要在长度相同的boards上重复判断是否可以裁切(判断一次就够了),比如程序中写的几种easy case。

     

    代码:

    /* ID: geterns1 PROG: fence8 LANG: C++ */ #include <iostream> #include <fstream> #include <cstdio> #include <cmath> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; const int B_MAX = 50; const int F_MAX = 1023; const int F_LEN = 128 + 1; int bnum, fnum, board[B_MAX], fails[F_MAX], fsum[F_MAX]; inline void qSwap(int &a, int &b){ a ^= b; b ^= a; a ^= b; } //search solution to cut 0~upIdx fails from lwIdx~bnum boards bool found; void ID_DFS(int upIdx, int lwIdx){ found = false; //Two easy case to end the search if (upIdx < 0) { found = true; return; } for (int i = 0; i < bnum; i ++){ if (fsum[upIdx] <= board[i]){ found = true; return; } } //A easy case that find no solution int sum = 0; for (int i = 0; i < bnum; i ++) if (board[i] >= fails[0]) sum += board[i]; if (sum < fsum[upIdx]) return; //If there is a best way to cut fails[upIdx], a very important pruning!!! for (int i = 0; i < bnum; i ++){ if (board[i] == fails[upIdx]){ board[i] = 0; //try to cut it if (fails[upIdx] == fails[upIdx - 1]) ID_DFS(upIdx - 1, i + 1); //special case, start search here directly else ID_DFS(upIdx - 1, 0); //deeper search board[i] = fails[upIdx]; //remember to recover it for another search!!! return; } } //Similarly, but no best way, just try one by one for (int i = lwIdx; i < bnum; i ++){ //no need to search boatds in same length again if (i && board[i] == board[i - 1]) continue; // if (board[i] > fails[upIdx]){ board[i] -= fails[upIdx]; //try to cut it if (fails[upIdx] == fails[upIdx - 1]) ID_DFS(upIdx - 1, i); //special case, start search here directly else ID_DFS(upIdx - 1, 0); //deeper search board[i] += fails[upIdx]; //remember to recover it for another search!!! if (found) return; //answer found, end up } } } //binary search to speed up the program int BS(int x, int y){ //break out case if (x == y) return x; if (y - x == 1){ ID_DFS(y, 0); if (found) return y; return x; } // int m = (x + y) / 2; ID_DFS(m, 0); if (found) return BS(m, y); return BS(x, m - 1); } int main(){ freopen("fence8.in", "r", stdin); freopen("fence8.out", "w", stdout); // scanf("%d", &bnum); for (int i = 0; i < bnum; i ++) scanf("%d", board + i); //Sort it in descending order int mhlp; for (int i = 0; i < bnum; i ++){ mhlp = i; for (int j = i + 1; j < bnum; j ++) if (board[mhlp] < board[j]) mhlp = j; if (mhlp ^ i) qSwap(board[mhlp], board[i]); } //Sort it in ascending order int bucket[F_LEN], bhlp; memset(bucket, 0, sizeof(bucket)); scanf("%d", &fnum); for (int i = 0; i < fnum; i ++){ scanf("%d", &bhlp); bucket[bhlp] ++; } bhlp = 0; for (int i = 1; i < F_LEN; i ++) for (int j = 0; j < bucket[i]; j ++) fails[bhlp ++] = i; //Sum the fails fsum[0] = fails[0]; for (int i = 1; i < fnum; i ++) fsum[i] = fsum[i - 1] + fails[i]; // if (fails[0] > board[0]) printf("0/n"); else printf("%d/n", BS(0, fnum - 1) + 1); fclose(stdin); fclose(stdout); return 0; }


    最新回复(0)