POJ 2718 Smallest Difference (穷竭搜索+剪枝+全排列生成)

问题:Smallest Difference POJ - 2718

给定若干位十进制数,你可以通过选择一个非空子集并以某种顺序构建一个数。剩余元素可以用相同规则构建第二个数。除非构造的数恰好为0,否则不能以0打头。 举例来说,给定数字0,1,2,4,6与7,你可以写出10和2467。当然写法多样:210和764,204和176,等等。最后一对数差的绝对值为28,实际上没有其他对拥有更小的差

Input:
输入第一行的数表示随后测试用例的数量。
对于每组测试用例,有一行至少两个不超过10的十进制数字。(十进制数字为0,1,…,9)每行输入中均无重复的数字。数字为升序给出,相隔恰好一个空格。
Output:
对于每组测试用例,输出一个以上述规则可获得的最小的差的绝对值在一行。
Sample Input - 输入样例
1
0 1 2 4 6 7
Sample 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;
}

猜你喜欢

转载自blog.csdn.net/qq_41333528/article/details/80141797