原题链接:
http://poj.org/problem?id=2718
题意:
给你几个数字,可以分成两个子集,然后分别按一定顺序排列组成一个数,求出这两只值差的绝对值的最小值。
分析:
反正也是刷着玩,果断先交一波全排列枚举的代码,果断TLE,然后开始想正解。
稍微想想,既然要差最小,肯定是两个数各一半。所以只要深搜出所有n/2(n为给定数字的个数)的组合,另外个n-n/2个数就有了。
但是枚举出来后的操作又想了很久,想过很多算法,都不怎么满意,最终用二分解决。
先把n/2和n-n/2全排列,算了出所有可能组成的整数,然后进行二分,分别和最近的作差比较,需要注意特殊处理。
坑点:忘记了单个0需要特殊考虑,导致WA了好几发,马蛋。
最坏时间复杂度(n==10):C(5,10)×(5!×5)×2==302400。跑了32ms。
算是很快了,不过肯定有比我更快的,膜拜各路大神。
代码:
1 #include <iostream> 2 #include <sstream> 3 #include <cstdio> 4 #include <cstring> 5 #include <vector> 6 #include <set> 7 #include <map> 8 #include <algorithm> 9 #include <string> 10 #include <queue> 11 #include <cmath> 12 #include <stack> 13 #include <cctype> 14 #include <list> 15 16 #define ll long long 17 #define ull unsigned long long 18 #define VNAME(name) (#name) 19 #define debug(a) cout<<VNAME(a)<<" = "<<(a)<<endl; 20 21 22 23 using namespace std; 24 25 const int maxn = 110; 26 const int inf = 1 << 30; 27 28 string str; 29 int num[maxn]; 30 int a[maxn],b[maxn];//保存在深搜中组合出的数列 31 bool vis[maxn];//回溯标记 32 int n; 33 int ans; 34 35 //全排列算出每个组成的整数 36 int make_permutation(int x,int *org,int *ne,int *num){ 37 for(int i=0;i<x;i++){ 38 ne[i]=org[i]; 39 } 40 sort(ne,ne+x); 41 int nn=0; 42 do{ 43 if(ne[0]==0)continue;//0前导忽略 44 int p=0; 45 for(int i=0;i<x;i++){ 46 p=p*10+ne[i]; 47 } 48 num[nn++]=p; 49 }while(next_permutation(ne,ne+x)); 50 return nn; 51 } 52 53 void dfs(int x,int pre){ 54 if(x==n/2){ 55 int y=n-n/2;//另一半 56 for(int i=0,j=0;j<y;i++){ 57 if(!vis[i]){ 58 b[j++]=num[i]; 59 } 60 } 61 int ta[11],tb[11];//暂存的a和b 62 int ra[130],rb[130];//保存组成的整数 63 int ran=make_permutation(x,a,ta,ra); 64 int rbn=make_permutation(y,b,tb,rb); 65 for(int i=0;i<ran;i++){ 66 int p=lower_bound(rb,rb+rbn,ra[i])-rb; 67 if(rb[p]==ra[i]){//特殊处理 68 ans=min(ans,0); 69 } 70 else if(p==0){//特殊处理 71 ans=min(ans,abs(rb[p]-ra[i])); 72 } 73 else if(p==rbn){//特殊处理 74 ans=min(ans,abs(rb[p-1]-ra[i])); 75 } 76 else{ 77 ans=min(ans,min(abs(rb[p-1]-ra[i]),abs(rb[p]-ra[i]))); 78 } 79 } 80 return ; 81 } 82 for(int i=pre;i<n;i++){ 83 if(!vis[i]){ 84 vis[i]=1; 85 a[x]=num[i]; 86 dfs(x+1,i+1); 87 vis[i]=0;//回溯 88 } 89 } 90 } 91 92 93 int main() { 94 iostream::sync_with_stdio(false); 95 96 #ifndef ONLINE_JUDGE 97 freopen("data.in","r",stdin); 98 //freopen("data.out","w",stdout); 99 #endif 100 101 int t; 102 cin>>t; 103 cin.get(); 104 while(t--) { 105 getline(cin,str); 106 stringstream ss(str); 107 n=0,ans=inf; 108 memset(vis,0,sizeof(vis)); 109 while(ss>>num[n]){ 110 n++; 111 } 112 //因为小于10的数,单个0不算前导,特殊考虑 113 //并且只有在2个数的时候才会出现这种情况,很容易发现 114 if(n==2){ 115 cout<<abs(num[0]-num[1])<<endl; 116 continue; 117 } 118 dfs(0,0); 119 cout<<ans<<endl; 120 } 121 return 0; 122 }
时间: 2024-10-12 22:30:56