组合商品问题
问题描述:http://blog.csdn.net/weixin_33226456/article/details/79506439
测试 test1.txt
3,3 2,3,1 bom1,2,1,1 bom2,1,1,0 bom3,0,1,1
代码框架 bom-frame.js(帮你做掉输入输出)
1 // read from stdin 2 process.stdin.setEncoding('utf8') 3 let input = '' 4 process.stdin.on('readable', () => { 5 const chunk = process.stdin.read() 6 if (chunk !== null) { 7 input += chunk 8 } 9 }) 10 process.stdin.on('end', () => { 11 const param = parseParam(input) 12 const answer = solve(param.items, param.boms) 13 console.log('match result:\n' + formatAnswer(answer, param.names)) 14 }) 15 16 // format answer 17 function formatAnswer(c_answer, c_names) { 18 return c_answer 19 .map((value, i)=>({idx:i,val:value})) 20 .filter(x=>x.val>0) 21 .map(x=>c_names[x.idx]+'*'+x.val) 22 .join(',') 23 } 24 25 // parse input params 26 function parseParam(c_input) { 27 const param = {} 28 const lines = c_input.split('\n') 29 const counts = lines[0].split(',') 30 const nItem = Number(counts[0]) 31 const nBom = Number(counts[1]) 32 const boms = lines.slice(2, 2 + nBom).map(x=>x.split(',')) 33 param.nItem = nItem 34 param.nBom = nBom 35 param.items = lines[1].split(',').map(x=>Number(x)) 36 param.boms = boms.map(x=>x.slice(1).map(x=>Number(x))) 37 param.names = boms.map(x=>x[0]) 38 return param 39 } 40 41 // ======== IGNORE CODES ABOVE FOR I/O ======== 42 const maxNItem = 10 43 const maxNBom = 9 44 // ======== FOCUS ON SOLUTION BELOW ======== 45 /* c_items: number of each item in order 46 * c_boms: list of boms, each item is a list of 47 */ 48 function solve(c_items, c_boms) { 49 return Array(c_boms.length).fill(0) // ### replace with your own code. 50 }
完整解答 bom.js
1 // read from stdin 2 process.stdin.setEncoding('utf8') 3 let input = '' 4 process.stdin.on('readable', () => { 5 const chunk = process.stdin.read() 6 if (chunk !== null) { 7 input += chunk 8 } 9 }) 10 process.stdin.on('end', () => { 11 const param = parseParam(input) 12 const answer = solve(param.items, param.boms) 13 console.log('match result:\n' + formatAnswer(answer, param.names)) 14 }) 15 16 // format answer 17 function formatAnswer(c_answer, c_names) { 18 return c_answer 19 .map((value, i)=>({idx:i,val:value})) 20 .filter(x=>x.val>0) 21 .map(x=>c_names[x.idx]+'*'+x.val) 22 .join(',') 23 } 24 25 // parse input params 26 function parseParam(c_input) { 27 const param = {} 28 const lines = c_input.split('\n') 29 const counts = lines[0].split(',') 30 const nItem = Number(counts[0]) 31 const nBom = Number(counts[1]) 32 const boms = lines.slice(2, 2 + nBom).map(x=>x.split(',')) 33 param.nItem = nItem 34 param.nBom = nBom 35 param.items = lines[1].split(',').map(x=>Number(x)) 36 param.boms = boms.map(x=>x.slice(1).map(x=>Number(x))) 37 param.names = boms.map(x=>x[0]) 38 return param 39 } 40 41 // ======== IGNORE CODES ABOVE FOR I/O ======== 42 const maxNItem = 10 43 const maxNBom = 9 44 // ======== FOCUS ON SOLUTION BELOW ======== 45 /* c_items: number of each item in order 46 * c_boms: list of boms, each item is a list of 47 * 48 * 1. Solution can be represented by *vec*(vector of boms used). 49 * *vec* is an vector in space N^*nBom*. 50 * 2. a viable *vec* gives a sulution in which sum of any item 51 * in all boms is not greater than that given by *items*. 52 * 3. There must be at least one solution with minimal rest item types 53 * on the edge of viable region. (But without some of these bom types may 54 * get result with less bom types (better solution for this problem)) 55 * Edge means adding any bom to the *vec* will result in a nonviable *vec*. 56 * i.e. we can find the best solution in the set: number of each bom is 57 * either 0 or the max number with other bom number fixed. 58 * (prove by yourself) 59 * 4. Traversal near the edge can find the answer. 60 * 5. Use sparse matrix to save space if you use DP.(I am not using DP here, 61 * but this is a suggestion for your implementation) 62 * 6. This is not the fastest algorithm for this problem since there are still 63 * some ways to cut more branches while traversing for best solution. 64 */ 65 function solve(c_items, c_boms) { 66 // init to origin 67 const vec = Array(c_boms.length).fill(0) 68 const rest = c_items.slice() 69 // lets first build a inverted index for finding related boms later 70 const rel = c_boms.reduce((sum, cur, idx)=>cur.reduce((s,c,i)=>{ 71 if (c > 0) { 72 if (s[i] != undefined) { 73 s[i].push(idx) 74 } else { 75 s[i] = [idx] 76 } 77 } 78 return s 79 },sum),{}) 80 // recursive part 81 const best = solveR(vec, 0, null, rest, c_boms, rel) 82 return best.vec 83 } 84 85 /* recursive part: 86 * With first *fixed* bom numbers fixed, find best vec 87 */ 88 function solveR(c_vec, c_fixed, c_best, c_rest, c_boms, c_rel) { 89 // case 0: end of recursion 90 let vec = c_vec.slice() 91 if (c_fixed == c_boms.length) { 92 let rest = c_rest.slice() 93 removeHelplessBom(vec, rest, c_boms) 94 const scr = score(vec, rest) 95 if (c_best == null || scr < c_best.score) { 96 return {score: scr, vec: vec} 97 } else { 98 return c_best 99 } 100 } 101 // case 1: not all bom numbers fixed 102 let distToEdge = c_boms[c_fixed].reduce((s, x, i) => { 103 if (x == 0) { 104 return s 105 } 106 let distI = Math.floor(c_rest[i] / x) 107 return s == null ? distI : s <= distI ? s : distI 108 }, null) 109 vec[c_fixed] += distToEdge; 110 let rest = calRest(c_rest, c_fixed, distToEdge, c_boms) 111 let best = solveR(vec, c_fixed + 1, c_best, rest, c_boms, c_rel) 112 // check if we need to test cases with less *c_fixed*th bom 113 let tryReduce = c_boms[c_fixed].some( 114 (x, i) => x > 0 && c_rel[i].filter(x => x > c_fixed).length > 0 115 ) 116 while(vec[c_fixed] > 0) { 117 vec[c_fixed] -- 118 rest = calRest(rest, c_fixed, -1, c_boms) 119 best = solveR(vec, c_fixed + 1, best, rest, c_boms, c_rel) 120 } 121 return best 122 } 123 124 // update rest value when *i*th com increase n 125 function calRest(c_rest, c_i, c_n, c_boms) { 126 const newRest = c_rest.slice() 127 return c_boms[c_i].reduce((sum,cur,idx)=>{sum[idx]-=cur*c_n;return sum}, newRest) 128 } 129 130 /* Let's call the boms which none of their related item types 131 * is completely covered by boms "helpless" (helpless for better score). 132 */ 133 function removeHelplessBom(vec, v_rest, c_boms) { 134 let rest = v_rest.slice(); 135 vec.forEach((veci, i) => { 136 if (veci > 0) { 137 let helpful = c_boms[i].some((val, idx) => val > 0 && rest[idx] == 0) 138 if (!helpful) { 139 vec[i] = 0 140 rest = calRest(rest, i, -1 * veci, c_boms) 141 } 142 } 143 }) 144 v_rest.length = 0 145 v_rest.push(...rest) 146 } 147 148 // score a solution, the lower the better 149 function score(c_vec, c_rest) { 150 const nUsedBomType = c_vec.filter(x=>x>0).length 151 const nRestType = c_rest.filter(x=>x>0).length 152 return nRestType * (maxNBom + 1) + nUsedBomType 153 }