[4_1_fence8] Search problem: Optimizations
来源:互联网 发布:js怎么定义全局函数 编辑:程序博客网 时间:2024/06/16 22:20
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 railSAMPLE 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:
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]); }}
- [4_1_fence8] Search problem: Optimizations
- problem encountered during text search
- Direct3D Performance Optimizations
- Handling IRPs 10: Optimizations
- Controlling Switchable Optimizations
- Easy UITableView optimizations
- Performance Optimizations (Direct3D 9)
- LLVM+WCET optimizations
- Optimizations On Mobile[Unity]
- Android M -- Power-Saving Optimizations
- Eclipse-"File Search" has encounter a problem
- Eclipse-"File Search" has encounter a problem
- Eclipse-'File Search' has encounter a problem
- Eclipse-"File Search" has encounter a problem
- Word Search Problem - Non-recursive Solution
- Eclipse-"File Search" has encounter a problem
- Eclispe:file search has encountered a problem
- [LeetCode]problem 35. Search Insert Position
- SqlServer2008学习笔记 基础知识
- C语言类型装换
- cocos2d-x 主角向任意方向使其同速移动的实现方法
- DOTNETBAR的使用方法(转)
- 实现TQ2440开发板与Linux虚拟机共享文件
- [4_1_fence8] Search problem: Optimizations
- Android中 Error generating final archive: Debug Certificate expired on 10/09/18 16:30 的错误
- 操作系统概论
- 第七周 任务五
- 如何关闭任务栏(状态栏)上的搜索桌面?
- 第二届华中区程序设计邀请赛暨武汉大学第十一届校赛 网络预选赛 解题报告
- Java Bean note
- my lovo之my sql
- Android 彩信还原到数据库