POJ 1416 -- Shredding Company
POJ 1416 -- Shredding Company
题意:
公司现在要发明一种新的碎纸机,要求新的碎纸机能够把纸条上的数字切成最接近而不超过target值。比如,target的值是50,而纸条上的数字是12346,应该把数字切成四部分,分别是1、2、34、6。因为这样所得到的和43 (= 1 + 2 + 34 + 6) 是所有可能中最接近而不超过50的。(比如1, 23, 4, 和6 就不可以,因为它们的和不如43接近50,而12, 34, 6也不可以,因为它们的和超过50了。碎纸还有以下三个要求:
1、如果target的值等于纸条上的值,则不能切。
2、如果没有办法把纸条上的数字切成小于target,则输出error。如target是1而纸条上的数字是123,则无论你如何切得到的和都比1大。
3、如果有超过一种以上的切法得到最佳值,则输出rejected。如target为15,纸条上的数字是111,则有以下两种切法11、1或者1、11.
你的任务是编写程序对数字进行划分以达到最佳值。
解题思路:
DFS
(1)比如一个6位数n,切成为6个数的话,这6个数的和如果大于目标数target则不用再搜索了,因为这肯定是所有划分中和最小的,而最小都比目标数大,自然就没有合要求的答案了.
(2) 如何切分,假如以50 12346 为例。
第一步,先切下一个“1”,然后递归去切“2346”;
第二步,再切下一个“12”,然后递归去切“346”;
第三步,再切下一个“123”,然后递归去切“46”;
第四步,再切下一个“1234” 然后递归去切“6”
第五步,再切下“12346”。
(3)切下来的 前面的数字串部分 则加入到划分的和,剩下的部分继续递归,直到已经划分出来的数字长度len等于数字串总长度strlen(num)。为了记录划分方式,使用int数组devide记录每一次划分的长度,比如样例50 12346划分为43 1 2 34 6,那devide[0] = 1,devide[1] = 1,devide[2] = 2,devide[3] = 1。而且为了保存最优解,需要在已经划分出来的数字长度len等于数字串总长度strlen(num),并且abs(bestx-target)>abs(sum-target)时,更新最优解bestx = sum,并且将devide转存到ansdevide中,为了方便之后答案的输出。
(4)使用字符数组存储数字串num,这样在搜索时,只要控制字符数组的起止位置就可以模拟数字串的拆分。
(5)剪枝方法:在搜索时若发现部分和 大于(不能等于)target时,则可结束搜索。
在进入dfs搜索时,可以把特殊情况排除
1)error的判定要在搜索前进行
2)当数字串num和target相等时,直接输出,不用划分
3)当数字串的每一位数字之和sum2等于target时,直接逐位输出,每个数字为一个划分
注意这种情况有一个特例,就是0出现的时候,
例如样例:6 1104
它的最优划分有两种 6 1 1 0 4
6 1 1 04
所以此样例应该输出rejected
所以这种情况下,应该保证数字串num中没有0,才可以直接逐位输出
(6)rejected(多个最优解)的判定要在搜索后判定。当最优解出现两次的时候(出现abs(bestx-target)==abs(sum-target)),使用全局变量Count计数
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int target; 6 char num[10]; 7 int bestx; 8 int Count; 9 int anslen; 10 int ansdevide[10]; 11 void dfs(char *ch,int len,int sum,int c,int *devide) 12 {///len为当前已经划分的数字串的长度,sum为当前已划分的数字的总和 13 ///c为划分的数字的块数 14 if(len == strlen(num)) 15 { 16 if(abs(bestx-target)>abs(sum-target)) 17 { 18 Count=0; 19 anslen = c; 20 bestx = sum; 21 for(int i=0;i<c;i++) 22 ansdevide[i] = devide[i]; 23 return; 24 } 25 if(abs(bestx-target)==abs(sum-target))///出现多个最优解 26 { 27 Count++; 28 } 29 return; 30 } 31 for(int i=1;i<=strlen(ch);i++) 32 {///确定下一步划分的长度i 33 int newsum = 0; 34 for(int j=0;j<i;j++) 35 newsum = newsum*10 + (ch[j]-'0'); 36 newsum+=sum; 37 devide[c] = i; 38 if(newsum<=target) 39 dfs(ch+i,len+i,newsum,c+1,devide); 40 } 41 } 42 43 int main() 44 { 45 while(cin>>target && target) 46 { 47 cin>>num; 48 int sum1 = 0;///计算纸上的数是多少 49 int sum2 = 0;///每一位的和 50 for(int i=0;i<strlen(num);i++) 51 { 52 sum1 = sum1*10+(num[i]-'0'); 53 sum2 += num[i]-'0'; 54 } 55 if(sum1 == target) 56 cout<<target<<" "<<target<<endl; 57 else if(sum2 > target) 58 { 59 cout<<"error"<<endl; 60 } 61 else if(sum2 == target && !strchr(num,'0')) 62 { 63 cout<<target; 64 for(int i=0;i<strlen(num);i++) 65 { 66 cout<<" "<<num[i]; 67 } 68 cout<<endl; 69 }else 70 { 71 Count = 0; 72 bestx = 0; 73 int devide[10]; 74 dfs(num,0,0,0,devide); 75 if(Count > 0) 76 cout<<"rejected"<<endl; 77 else{ 78 cout<<bestx; 79 int temp=0; 80 for(int i=0;i<anslen;i++) 81 { 82 cout<<" "; 83 for(int j=temp;j<temp+ansdevide[i];j++) 84 { 85 cout<<num[j]; 86 } 87 temp += ansdevide[i]; 88 } 89 cout<<endl; 90 } 91 } 92 93 } 94 return 0; 95 }