[4_1_fence8] Search problem: Optimizations

来源:互联网 发布:js怎么定义全局函数 编辑:程序博客网 时间:2024/06/16 22:20
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 becut 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 boardsLine 2..N+1:N lines, each containing a single integer that represents the length of one supplied boardLine N+2:R (1 <= R <= 1023), the number of railsLine 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)

4304050251015161718192021252430

OUTPUT FORMAT

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

SAMPLE OUTPUT (file fence8.out)

7











Solution 1: DFS search

passed 3 tests

#include <fstream>using namespace std;void dfs(const int n, const int rnum, const int r[], const int depth, const int cnt,         int rem[], int* const p_ans) {        if (depth == rnum) {                if (cnt > (*p_ans))                        *p_ans = cnt;                return;        }        dfs(n, rnum, r, depth + 1, cnt, rem, p_ans);        for (int i = 0; i < n; ++i)                if (rem[i] >= r[depth]) {                        rem[i] -= r[depth];                        dfs(n, rnum, r, depth + 1, cnt + 1, rem, p_ans);                        rem[i] += r[depth];                }}int main() {        ifstream fin("fence8.in");        int n;        fin >> n;        int s[50], rem;        for (int i = 0; i < n; ++i)                fin >> s[i];        int rnum;        fin >> rnum;        int r[1024];        for (int i = 0; i < rnum; ++i)                fin >> r[i];        fin.close();        int ans = 0;        dfs(n, rnum, r, 0, 0, s, &ans);        ofstream fout("fence8.out");        fout << ans << '\n';        fout.close();        return 0;}


Many optimization ideas provided here: http://www.nocow.cn/index.php/USACO/fence8 and http://apps.topcoder.com/forums/?module=Thread&threadID=505915&start=0&mc=9#506775

About IDDFS: http://en.wikipedia.org/wiki/Iterative_deepening_depth-first_search

About Knapsack problem: http://en.wikipedia.org/wiki/Knapsack_problem

Another multi-dimensional knapsack problem: USACO 3_4_rockers


Solution 2: IDDFS + Cut longest rails first

passed 4 tests

IDEA: Check the 4th test input file, it is easy to come up with optimization 1, which is to reduce the number of rails by ignoring rails whose lengths are longer than max-length board. However, it still cannot pass the tests. Thus, it is better to transform the exhaustive DFS search into IDDFS and check whether it is able to cut out 1 rail, 2 rails, 3 rails, ... (optimization 2)

In this IDDFS case, it is natural to check only the shortest required rails (optimization 3). Further, for the rails of same sizes, there is no need to try smaller-numbered boards which have been tested by previous rails before (optimization 4).

Sadly, this is still not enough. Hence, optimization 5 should be applied.

#include <algorithm>#include <fstream>using namespace std;const bool check(const int n, const int r[], const int depth, const int idx,           int board[]) {        if (depth < 0)                return true;        // OPTIMIZATION 4: For same-size rails, do not try previous boards (POWERFUL)        for (int i = (r[depth] == r[depth + 1]) ? idx : 0; i < n; ++i)                if (board[i] >= r[depth]) {                        board[i] -= r[depth];                        const bool cutable = check(n, r, depth - 1, i, board);                        board[i] += r[depth];                        if (cutable)                                return true;                }        return false;}int main() {        ifstream fin("fence8.in");        int n;        fin >> n;        int s[50];        for (int i = 0; i < n; ++i)                fin >> s[i];        int rnum;        fin >> rnum;        int r[1024];        for (int i = 0; i < rnum; ++i)                fin >> r[i];        fin.close();        // OPTIMIZATION 1: ignore invalid inputs (NOT USEFUL FOR IDDFS)        int maxs = 0;        for (int i = 0; i < n; ++i)                maxs = (maxs > s[i] ? maxs : s[i]);        sort(r, r + rnum);        while (r[rnum - 1] > maxs)                --rnum;        int ans = 0;        // OPTIMIZATION 2: iterative deepening DFS        // OPTIMIZATION 3: only check shortest required rails        // OPTIMIZATION 5: cut longest rails first (POWERFUL)        while (ans <= rnum && check(n, r, ans - 1, 0, s))                ++ans;        --ans;        ofstream fout("fence8.out");        fout << ans << '\n';        fout.close();        return 0;}


Solution 3: Calculating waste for pruning

passed 7 tests

#include <algorithm>#include <fstream>using namespace std;const bool check(const int n, const int r[], const int depth, const int idx, const int maxwaste,                 int board[], int waste) {        if (depth < 0)                return true;        // OPTIMIZATION 3: for same-size rails, do not try previous boards        for (int i = (r[depth] == r[depth + 1]) ? idx : 0; i < n; ++i)                if (board[i] >= r[depth]) {                        board[i] -= r[depth];                        // OPTIMIZATION 5: total waste should not exceed its maximum                        bool cutable = true;                        if (board[i] < r[0]) {                                waste += board[i];                                if (waste > maxwaste)                                        cutable = false;                        }                        if (cutable)                                cutable = check(n, r, depth - 1, i, maxwaste, board, waste);                        if (board[i] < r[0])                                waste -= board[i];                        board[i] += r[depth];                        if (cutable)                                return true;                }        return false;}int main() {        ifstream fin("fence8.in");        int n;        fin >> n;        int s[50], total = 0;        for (int i = 0; i < n; ++i) {                fin >> s[i];                total += s[i];        }        int rnum;        fin >> rnum;        int r[1024], sumr[1024];        for (int i = 0; i < rnum; ++i)                fin >> r[i];        fin.close();        int ans = 1;        // OPTIMIZATION 1: iterative deepening DFS        // OPTIMIZATION 2: only check shortest required rails        sort(r, r + rnum);        sumr[0] = r[0];        for (int i = 1; i < rnum; ++i)                sumr[i] = sumr[i - 1] + r[i];        // OPTIMIZATION 4: cut longest rails first        while (ans <= rnum && check(n, r, ans - 1, 0, total - sumr[ans - 1], s, 0))                ++ans;        --ans;        ofstream fout("fence8.out");        fout << ans << '\n';        fout.close();        return 0;}


Solution 4: Check for apparently invalid inputs

passed all 12 tests

#include <algorithm>#include <fstream>using namespace std;const bool check(const int n, const int r[], const int depth, const int idx, const int maxwaste,                 int board[], int waste) {        if (depth < 0)                return true;        // OPTIMIZATION 3: for same-size rails, do not try previous boards        for (int i = (r[depth] == r[depth + 1]) ? idx : 0; i < n; ++i)                if (board[i] >= r[depth]) {                        board[i] -= r[depth];                        // OPTIMIZATION 5: total waste should not exceed its maximum                        bool cutable = true;                        if (board[i] < r[0]) {                                waste += board[i];                                if (waste > maxwaste)                                        cutable = false;                        }                        if (cutable)                                cutable = check(n, r, depth - 1, i, maxwaste, board, waste);                        if (board[i] < r[0])                                waste -= board[i];                        board[i] += r[depth];                        if (cutable)                                return true;                }        return false;}int main() {        ifstream fin("fence8.in");        int n;        fin >> n;        int s[50], total = 0;        for (int i = 0; i < n; ++i) {                fin >> s[i];                total += s[i];        }        int rnum;        fin >> rnum;        int r[1024], sumr[1024];        for (int i = 0; i < rnum; ++i)                fin >> r[i];        fin.close();        int ans = 1;        // OPTIMIZATION 1: iterative deepening DFS        // OPTIMIZATION 2: only check shortest required rails        sort(r, r + rnum);        sumr[0] = r[0];        for (int i = 1; i < rnum; ++i)                sumr[i] = sumr[i - 1] + r[i];        // OPTIMIZATION 6: ignore obviously invalid inputs        while(sumr[rnum - 1] > total)                --rnum;        // OPTIMIZATION 4: cut longest rails first        while (ans <= rnum && check(n, r, ans - 1, 0, total - sumr[ans - 1], s, 0))                ++ans;        --ans;        ofstream fout("fence8.out");        fout << ans << '\n';        fout.close();        return 0;}

Results:

Executing...   Test 1: TEST OK [0.000 secs, 3360 KB]   Test 2: TEST OK [0.000 secs, 3360 KB]   Test 3: TEST OK [0.000 secs, 3360 KB]   Test 4: TEST OK [0.011 secs, 3360 KB]   Test 5: TEST OK [0.011 secs, 3360 KB]   Test 6: TEST OK [0.011 secs, 3360 KB]   Test 7: TEST OK [0.000 secs, 3360 KB]   Test 8: TEST OK [0.022 secs, 3360 KB]   Test 9: TEST OK [0.032 secs, 3360 KB]   Test 10: TEST OK [0.000 secs, 3360 KB]   Test 11: TEST OK [0.000 secs, 3360 KB]   Test 12: TEST OK [0.000 secs, 3360 KB]


Solution 5: Binary search for the answer

All tests 0.000 secs

#include <algorithm>#include <fstream>using namespace std;const bool check(const int n, const int r[], const int depth, const int idx, const int maxwaste,                 int board[], int waste) {        if (depth < 0)                return true;        // OPTIMIZATION 3: for same-size rails, do not try previous boards        for (int i = (r[depth] == r[depth + 1]) ? idx : 0; i < n; ++i)                if (board[i] >= r[depth]) {                        //if (i > 0 && board[i] == board[i - 1])                        //        continue;                        board[i] -= r[depth];                        // OPTIMIZATION 5: total waste should not exceed its maximum                        bool cutable = true;                        if (board[i] < r[0]) {                                waste += board[i];                                if (waste > maxwaste)                                        cutable = false;                        }                        if (cutable)                                cutable = check(n, r, depth - 1, i, maxwaste, board, waste);                        if (board[i] < r[0])                                waste -= board[i];                        board[i] += r[depth];                        if (cutable)                                return true;                }        return false;}int main() {        ifstream fin("fence8.in");        int n;        fin >> n;        int s[50], total = 0;        for (int i = 0; i < n; ++i) {                fin >> s[i];                total += s[i];        }        int rnum;        fin >> rnum;        int r[1024], sumr[1024];        for (int i = 0; i < rnum; ++i)                fin >> r[i];        fin.close();        // OPTIMIZATION 1: iterative deepening DFS        // OPTIMIZATION 2: only check shortest required rails        sort(r, r + rnum);        sumr[0] = r[0];        for (int i = 1; i < rnum; ++i)                sumr[i] = sumr[i - 1] + r[i];        // OPTIMIZATION 6: ignore obviously invalid inputs        while(sumr[rnum - 1] > total)                --rnum;        // OPTIMIZATION 7: binary search for answer        int low = 0, high = rnum, mid;        while (low < high) {                mid = (low + high + 1) >> 1;                // OPTIMIZATION 4: cut longest rails first                if (check(n, r, mid - 1, 0, total - sumr[mid - 1], s, 0))                        low = mid;                else                        high = mid - 1;        }        ofstream fout("fence8.out");        fout << low << '\n';        fout.close();        return 0;}





Official solutions:

Fence Rails
Hal Burch

This is a high dimensionality multiple knapsack problem, so we just have to test the cases. Given that the search space has a high out-degree, we will use depth first search with iterative deepening in order to limit the depth of the tree. However, straight DFSID will be too slow, so some tree-pruning is necessary.

Note that if there is a way to cut k rails, there is a way to cut the k shortest rails, so we will only consider subsets of the rails that contain the k shortest rails. Also, we know the sum or the rails cut cannot exceed the sum of the lengths of the rails, so we can stop our DFS-ID if it finds a way to cut the largest set of shortest rails such that the sum of the lengths of the rails is less than the sum of the board lengths.

Since finding a board from which to cut a longer rail is more difficult than finding a board for a shorter rail, we will perform the search in such that the longest rail is cut first, then the second longest rail, etc.

Also, if two rails are of the same length, then cutting the first from board A and the second from board B is the same as cutting the first from board B and the second from board A, so within sets of rails of the same length, we will ensure that the rails are cut from boards of non-decreasing index.

If there are two boards of the same length, we need to check cutting the rail from only the first.

If there is a board of the same length of the rail, then cutting that rail from that board is optimal.

If, when cutting a board, we get a length of board less than the shortest rail, it is useless, and we can discard it from consideration.This reduces the total amount of board-feet left from which to cut the rest of the rails.

Here is Reid Barton's solution, which might or might not match the above description (sorry).

/*ID: reid001PROG: fence8*/#include <stdio.h>#include <stdlib.h>#define MAXBOARDS 50#define MAXRAILS 1024int nboards;int boards[MAXBOARDS];int next_board;int nrails;int rails[MAXRAILS];int used[MAXRAILS];int nused;int best;int num_V, num_L, num_R;int comp_func( const void *a, const void *b );int rev_comp_func( const void *a, const void *b );void search( void );int maximal( int k, int next, int other, int smin, int smax, int remain[], int origid[], int bound );inline int max( int a, int b ){return (a < b) ? b : a;}int main( void ){FILE *input = fopen( "fence8.in", "r" );fscanf( input, "%d", &nboards );for (int i = 0; i < nboards; i++)fscanf( input, "%d", &boards[i] );fscanf( input, "%d", &nrails );for (int i = 0; i < nrails; i++)fscanf( input, "%d", &rails[i] );rails[nrails++] = 1000000;qsort( boards, nboards, sizeof(int), comp_func );qsort( rails, nrails, sizeof(int), comp_func );int ans;if (boards[nboards-1] >= 1000000)// the answer might be off by one if we could supply the big rail.// but then we could supply all the other rails, so no need to search.ans = nrails - 1;else{next_board = 0;search();ans = best;}FILE *output = fopen( "fence8.out", "w" );fprintf( output, "%d\n", ans );//fprintf( stderr, "%d %d %d %d\n", ans, num_V, num_L, num_R );return 0;}int comp_func( const void *a, const void *b ){const int *p = (const int *)a;const int *q = (const int *)b;if (*p < *q)return -1;else if (*p > *q)return 1;elsereturn 0;}int rev_comp_func( const void *a, const void *b ){return -comp_func( a, b );}void search( void ){if (next_board == nboards){if (best < nused)best = nused;//fprintf( stderr, "nused = %d best = %d\n", nused, best );return;}int nremain;int remain[MAXRAILS];int origid[MAXRAILS];// find remaining rails,// as well as max # for this board, all remaining boardsint boardsum = 0;for (int j = next_board; j < nboards; j++)boardsum += boards[j];nremain = 0;int k = 0, l = 0;for (int j = 0, sum = 0; j < nrails; j++)if (used[j] == 0){remain[nremain] = rails[j];origid[nremain] = j;nremain++;sum += rails[j];if (sum <= boards[next_board])k++;if (sum <= boardsum)l++;}int bound;if ((bound = nused + l) <= best)return;// try all maximal m-subsets of remaining boardsfor (int m = k; m >= 0; m--)maximal( m, l-1, nremain-1, 0, boards[next_board], remain, origid, bound );}int maximal( int k, int next, int other, int smin, int smax, int remain[], int origid[], int bound ){if (k == 0){if ((smin <= 0) && (0 <= smax)){next_board++;search();next_board--;}return 0;}if (k > next+1)return 0;// not enough boards leftnum_V++;int low_sum = 0;for (int j = 0; j < k; j++)low_sum += remain[j];if (low_sum > smax){num_L++;return 0;}int hi_sum = 0;for (int j = 0; j < k; j++)hi_sum += remain[next-j];if (hi_sum < smin){num_R++;return 0;}int last = other;for (int m = next; m >= k-1; m--){if (remain[m] != remain[last] && (low_sum - remain[k-1] + remain[m]) <= smax){int new_min = max( smin - remain[m], smax - remain[last] + 1 );used[origid[m]] = 1;nused++;int x = maximal( k-1, m-1, last, new_min, smax - remain[m], remain, origid, bound );used[origid[m]] = 0;nused--;if (k == 1)return 0;if (bound <= best)return 0;}last = m;}return 0;}


Hassan Eslami submitted a dramatically faster solution:

#include <cstdlib>#include <fstream>#include <algorithm>#include <iostream>using namespace std;const int maxboard=50+1;const int maxrail=1023+1;// the arrays contains rails and boards lenght'sint rail[maxrail], board[maxboard];// the array contains how much of the i_th board remain for cutting the rails fromint remain[maxboard];// number of boards and railsint nboard, nrail;// sumlen[i] contains sum of the length of first i rails in the case// that rails are in the sorted orderint sumlen[maxrail];// minindex[i] contains the index of smallest board that i_th rail// can cut from it(rails and boards are in the sorted order)int minindex[maxrail];//amount of board that 'can' be waste during one run of DSFlong long maywaste;//amount of board that waste yetint waste;//best solution that we want to findint best;ifstream fin("fence8.in");ofstream fout("fence8.out");//r is the rail that we want cut from board index1 or index1+1 or ... or nboard-1void DFS(int r, int index1){        //if r is equal to 0 we must search for the last step of solution    if (r == 0) {        for (int i=index1; i<nboard; ++i)            if (remain[i]>=rail[0]){                                //if we can cut best+1 rails, we print the solution                fout << best+1 << endl;                fout.close();                exit(0);            }        return;    }            for (int i=index1; i<nboard; ++i)                //for cutting r_th rail from i_th board this condition must hold        if (remain[i]>=rail[r]) {            int oldwaste=waste;            remain[i]-=rail[r];                //now we check if i_th board being useless, amount of    // wasted board must be less than 'maywaste'            if (remain[i]<rail[0] && waste+remain[i]>maywaste) {                remain[i]+=rail[r];                continue;            }                        // if i_th board being useless we must update 'waste'            if (remain[i]<rail[0]) waste+=remain[i];                // now we check if two rails have equal size, then the    // usage of board for them must be in non-decreasing order            if (rail[r-1] == rail[r]) DFS(r-1, i);            else DFS(r-1, minindex[r-1]);                        // at last we set the initial state again            remain[i]+=rail[r];            waste=oldwaste;        }}int main() {        // reading the input and compute sum of the boards length's,    // also set the initial state of 'remain'    fin >> nboard;    long long sum=0;    for (int i=0; i<nboard; ++i) {        fin >> board[i];        sum+=board[i];        remain[i]=board[i];    }    fin >> nrail;    for (int i=0; i<nrail; ++i)        fin >> rail[i];    fin.close();        // sort the rails and boards    sort(&board[0], &board[nboard]);    sort(&rail[0], &rail[nrail]);        // set maximum number of rails that we want to use, with out loss anything    int temp=0;    sumlen[0]=rail[0];    for (; temp<nrail && sumlen[temp]<=sum; ++temp, sumlen[temp]=sumlen[temp-1]+rail[temp]);    nrail=temp;        // set minindex array    for (int i=0, j=0; i<nrail; ++i) {        while (j<nboard && rail[i]>board[j]) j++;        minindex[i]=j;        if (j == nboard) {            nrail=i;            break;        }    }        // check out one special case    if (nrail == 0) {        fout << 0 << endl;        fout.close();        return 0;    }        // main part of code that use DFS+ID    for (int i=nrail-1; i>=0; --i){        waste=0;        maywaste=sum-sumlen[i];        best=i;        DFS(i, minindex[i]);    }}


原创粉丝点击