问题:Smallest Difference POJ - 2718
给定若干位十进制数,你可以通过选择一个非空子集并以某种顺序构建一个数。剩余元素可以用相同规则构建第二个数。除非构造的数恰好为0,否则不能以0打头。 举例来说,给定数字0,1,2,4,6与7,你可以写出10和2467。当然写法多样:210和764,204和176,等等。最后一对数差的绝对值为28,实际上没有其他对拥有更小的差
对于每组测试用例,有一行至少两个不超过10的十进制数字。(十进制数字为0,1,…,9)每行输入中均无重复的数字。数字为升序给出,相隔恰好一个空格。
1 0 1 2 4 6 7Sample Output - 输出样例
28
分析:
用getline先读入一行,再取出其中的数字,本题显然要枚举所有的可能,这个枚举过程可以等效为对这几个数全排列的枚举,虽然好像有c++里面的函数可以实现,这里姑且让我自己写一个,实际上也很简单:使用dfs框架,由于参数记录了当前已经填好了几个数,所以不用考虑撤销当前步骤去进行下一步,直接根据已经填了几个数填下一个数即可,填过的位置直接覆盖就好。直到填满所需的个数,就开始判断,如果要求差的绝对值的最小值,两个数的位数要么一样,要么就只相差1位,所以从中间断开比较大小即可。这里从数组里得到整数,可以像下面程序里面一位一位算,也可以对字符数组操作,使用atoi()(#include<cstdlib>),如果字符串使用string存储,就要使用string::c_str(),讲偏了。。。但是这样处理的话,8位以内速度都是很快的,九位十位就不行了,会超时,必须考虑剪枝。至于为什么会超时,需要注意10!=3628800,光是枚举全排列就有300万的情况,每一种情况在对应十几步的操作,就有可能摸到1亿的门槛了,更何况这只是一组数据。
考虑以下剪枝:
1.两个数的首位不能是0(有bug,如果只有两个数,其中之一是0,就不能剪这个枝,所以设定剪枝条件:数的个数>2)
2.如果数的个数是偶数,则设定第一个数的首位小于第二个数的首位,反正大于的话不过是重复计算;而且设定在数的个数>7的时候,首位差的绝对值必须为1
3.如果数的个数是奇数,则设定第一个数(偶数位)的首位大于第二个数的首位,只有这样才可以满足差的绝对值最小;而且设定在数的个数>7的时候,首位之差的绝对值必须>=7(这个下限要好好考虑啊,如果设定为8,则有反例:0 1 2 3 4 5 6 7 8和0 2 3 4 5 6 7 8 9,注意0不在首位)
4.剪枝1 2 3都要在完全生成全排列之前操作,不要再已经生成错误或没必要全排列之后再剪枝
其他感悟:
打表debug赛高!!!(打表的代码也保存在以下代码中)
代码:
#include<iostream> #include<string> #include<vector> #include<cmath> using namespace std; string s; vector<int> v; int num=0,tem[15],sst=1<<30; //int shu[]={0,1,2,3,4,5,6,7,8,9}; void dfs(int n){ if(n==num){ if(num%2==0){ int x=tem[0],y=tem[num/2]; if(num==10&&abs(x-y)>1&&x>y)return; for(int i=1;i<num/2;i++){ x*=10; x+=tem[i]; } for(int i=num/2+1;i<num;i++){ y*=10; y+=tem[i]; } if(abs(x-y)<sst)sst=abs(x-y); return; } if(num%2==1){ int x=tem[0],y=tem[num/2]; if(x<y)return; for(int i=1;i<num/2;i++){ x*=10; x+=tem[i]; } for(int i=num/2+1;i<num;i++){ y*=10; y+=tem[i]; } if(abs(x-y)<sst)sst=abs(x-y); return; } } if(n>0&&num>2&&tem[0]==0)return; if(n>num/2&&num>2&&tem[num/2]==0)return; if(n>num/2){ if(num%2==0){ if(tem[0]>tem[num/2])return; if(num>7&&abs(tem[0]-tem[num/2])>1)return; } else{ if(tem[0]<tem[num/2])return; if(num>7&&tem[0]-tem[num/2]<7)return; } } for(int i=0;i<v.size();i++){ int ok=1; for(int j=0;j<n;j++){ if(tem[j]==v[i])ok=0; } if(ok){ tem[n]=v[i]; dfs(n+1); } } } int main(){ int n; cin>>n; getline(cin,s); while(n--){ v.clear(); sst=1<<30; num=0; getline(cin,s); for(int i=0;i<s.size();i++){ if(s[i]!=' '){ num++; v.push_back(s[i]-'0'); } } /*for(int i=0;i<1024;i++){ v.clear();num=0;sst=1<<30; for(int j=0;j<10;j++){ if((i>>j)&1){ v.push_back(shu[j]); num++; } } dfs(0); //if(sst!=1<<30)continue; for(int j=0;j<v.size();j++){ cout<<v[j]<<' '; } cout<<":"<<sst<<endl; }*/ dfs(0); cout<<sst<<endl; } return 0; }