最小的差别

  题目大意:输入一串数字,问你数字的组合方式,你可以随意用这些数字组合(无重复)来组合成一些整数(第一位不能为0),然后问你组合出来的整数的差最小是多少?

  这一题可以用枚举的方法去做,这里我就用枚举和贪心(这个非常爽)的方法去。

  首先这一题的限制是1000ms,我们知道全枚举的时间肯定是超时的,所以我们必须裁枝,我们这样看,我们得到的最小值,必须是两个数差别最小的时候才会出现,所以这里我们可以只用枚举lenth/2时候的数串的情况,这样就降低了很多的复杂度。

  但是这样还是不够,但是我们知道,如果我们规定枚举的子串A一定比子串B大,则当枚举第一个的时候,如果枚举第二个的数的某个位(比如枚举012467,子串A为204,当子串B的百位枚举到7的时候)我们就可不必往十位和各位枚举了)这样也会缩减很多时间,还要把最高位是0的情况排除,这也是必须做的情况

  当然,这一题还有一些很特殊的情况我们要考虑,比如当只输入01的时候,我们可能得不到结果,因为这个时候ans可能还是INF,这些情况要单独考虑

  

 #include <iostream>
#include <functional>
#include <algorithm> using namespace std; //全部用全局定义,防止递归太深产生MLE
static bool used_A[];
static bool used_B[];
static int input[];
static int zero_set[] = { , , , , , };//补0最多补5个
static int valueA, cutA, cutB, ans, length; void DFS_A(const int, const int);
void DFS_B(const int, const int); int main(void)//双DFS法
{
int case_sum;
char tmp;
while (~scanf("%d", &case_sum))
{
getchar();//消掉回车
for (int i = ; i < case_sum; i++)
{
length = ;
while ((tmp = getchar()) != '\n')
{
if (tmp != ' ')
input[length++] = tmp - '';
}
ans = INT_MAX;
cutA = length / ;//length要砍一半
cutA = cutA>length - cutA ? cutA : length - cutA;
cutB = length - cutA;
valueA = ; memset(used_A, , sizeof(used_A)); DFS_A(, );
if (ans != INT_MAX)
cout << ans << endl;
else//这个情况是针对x 0的情况的
cout << valueA << endl;
}
}
return ;
} void DFS_A(const int sumA, const int level)
{
//函数DFS_A,枚举数段A的所有可能,默认A的大小要比B的大
if (sumA == && level == )//出现第一位是0的情况,不用再枚举下去了
return;
if (level== cutA)
{
valueA = sumA;
memset(used_B, , sizeof(used_B));
DFS_B(, );
}
else if (level < cutA)
{
for (int i = ; i < length; i++)
{
if (!used_A[i])
{
used_A[i] = ;
DFS_A(sumA * + input[i], level + );
used_A[i] = ;//回溯
}
}
}
} void DFS_B(const int sumB, const int level)
{
//函数DFS_B,枚举数段B的所有可能,并对数值进行裁枝
if (sumB == && level == )//出现第一位是0的情况,不用再枚举下去了
return;
int tmp = sumB * zero_set[cutB - level];
if (tmp >= valueA)//默认A的大小要比B的大
return;
else if (cutB == level)
ans = min(ans, valueA - sumB);
else if (level < cutB)
{
for (int i = ; i < length; i++)
{
if (!used_A[i] && !used_B[i])
{
used_B[i] = ;
DFS_B(sumB * + input[i], level + );
used_B[i] = ;//回溯
}
}
}
}

  但是这一份代码

  在评测机跑了875MS,我不开心,怎么会那么慢?

  其实一开始参考了这里http://blog.csdn.net/z309241990/article/details/19548297的方法,回过头来再看别人的判断条件

 void DFS_B(const int sumB, const int level)
{
//函数DFS_B,枚举数段B的所有可能,并对数值进行裁枝
if (sumB == && level == )//出现第一位是0的情况,不用再枚举下去了
return;
int tmp = sumB * zero_set[cutB - level];
if (tmp >= valueA
&& (ans <= abs(valueA - tmp) && level > ))
return;
else if (cutB == level)
ans = min(ans, abs(valueA - sumB));
else if (level < cutB)
{
for (int i = ; i < length; i++)
{
if (!used_A[i] && !used_B[i])
{
used_B[i] = ;
DFS_B(sumB * + input[i], level + );
used_B[i] = ;//回溯
}
}
}
}

  

  很好变成375MS,减了差不多一半

  后来想了一下,我这个tmp>=valueA真是画蛇添足,根本就不需要,其实ans<=min(ans,abs(valueA-sumB))这个条件就已经排除了对称的情况了

  比如如果一开始已经出了A:176和B:204的差值28,那么下一次A:204和B:167(注意我的顺序),就直接不用枚举176,可能你会问,不是还会有176的时候的更小的值吗?可是这个情况,绝对会被对称的情况优先排除,所以是不用考虑的

  最后代码:

  

 #include <iostream>
#include <functional>
#include <algorithm> using namespace std; //全部用全局定义,防止递归太深产生MLE
static bool used_A[];
static bool used_B[];
static int input[];
static int zero_set[] = { , , , , , };//补0最多补5个
static int valueA, cutA, cutB, ans, length; void DFS_A(const int, const int);
void DFS_B(const int, const int); int main(void)//双DFS法
{
int case_sum;
char tmp;
while (~scanf("%d", &case_sum))
{
getchar();//消掉回车
for (int i = ; i < case_sum; i++)
{
length = ;
while ((tmp = getchar()) != '\n')
{
if (tmp != ' ')
input[length++] = tmp - '';
}
ans = INT_MAX;
cutA = length / ;//length要砍一半
cutB = length - cutA;
memset(used_A, , sizeof(used_A)); DFS_A(, );
if (ans != INT_MAX)
cout << ans << endl;
else//这个情况是针对x 0的情况的
cout << valueA << endl;
}
}
return ;
} void DFS_A(const int sumA, const int level)
{
//函数DFS_A,枚举数段A的所有可能,默认A的大小要比B的大
if (sumA == && level == )//出现第一位是0的情况,不用再枚举下去了
return;
if (level== cutA)
{
valueA = sumA;
memset(used_B, , sizeof(used_B));
DFS_B(, );
}
else if (level < cutA)
{
for (int i = ; i < length; i++)
{
if (!used_A[i])
{
used_A[i] = ;
DFS_A(sumA * + input[i], level + );
used_A[i] = ;//回溯
}
}
}
} void DFS_B(const int sumB, const int level)
{
//函数DFS_B,枚举数段B的所有可能,并对数值进行裁枝
if (sumB == && level == )//出现第一位是0的情况,不用再枚举下去了
return;
int tmp = sumB * zero_set[cutB - level];
if (ans <= abs(valueA - tmp) && level > )//如果补0以后大小都比ans大了,不用再搜了
return;
else if (cutB == level)
ans = min(ans, abs(valueA - sumB));
else if (level < cutB)
{
for (int i = ; i < length; i++)
{
if (!used_A[i] && !used_B[i])
{
used_B[i] = ;
DFS_B(sumB * + input[i], level + );
used_B[i] = ;//回溯
}
}
}
}

  这个已经很快了

  但是这个还可以用贪心去做,但是做起来异常复杂容易出错,不过我还是去做了这个异常复杂的贪心算法(其实也不算复杂),哈哈哈!

  那怎么做呢?

  主要思路就是,这个串是顺序的,如果是偶数,那么就找相邻差值最小的两个数(不包括0),假设两个子串的代表整数位n和m(n>m)那么这两个相邻第i+1个整数就作为n的最高位,i个整数作为m的最高位,然后从最小到大(包括0)填充n的剩余位置,从大到小填充m的剩余位置(如果存在两个相邻元素的差值相等,我们要把这些相邻元素都找出来并且做相同的步骤,最后比较得出的最小值)

  如果是奇数,那么我们就把第一个不为0的数作为n的最高位,倒数最后一个数作为m的最高位,然后还是从小到大填充n的剩余位置,从大到小填充m的剩余位置(一定要注意千万不要数组越界,我就吃过这样的亏)

  

 #include <iostream>
#include <functional>
#include <algorithm> using namespace std; static int input[];
static int length, cutA, cutB;
static int zero_set[] = { , , , , , }; int cal(const int, const int);
void cal_odd(const int, const int); int fcmop(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
} int main(void)
{
int case_sum, tmp; scanf("%d", &case_sum);
getchar();
for (int i = ; i < case_sum; i++)
{
length = ;
while ((tmp = getchar()) != '\n')
{
if (tmp != ' ')
input[length++] = tmp - '';
}
qsort(input, length, sizeof(int), fcmop);
cutA = length / ;
cutA = cutA > length - cutA ? cutA : length - cutA;
cutB = length - cutA;
if (length == )
cout << << endl;
else if (length == )
cout << input[] << endl;
else if (length == )
cout << abs(input[] - input[]) << endl;
else
{
int min_delta, min_sum;
if (input[] != )
{
if (cutA == cutB)//如果两个子串的长度相同,则取差值最小的相邻数,大的作为n的最高位,小的作为m的最高位,n的剩下位从小的补齐,m从大的补齐
{
min_sum = INT_MAX;
min_delta = input[] - input[];
for (int i = ; i < length - ; i++)
{
if (input[i + ] - input[i] < min_delta)
{
min_delta = input[i + ] - input[i];
min_sum = min(min_sum, cal(i + , i));
}
else if (input[i + ] - input[i] == min_delta)
min_sum = min(min_sum, cal(i + , i));
}
cout << min_sum << endl;
}
else cal_odd(, length - );
}
else//input[0]==0;
{
if (cutA == cutB)//如果字串长度相同,A-B则A的最高位要比B的最高位的差值要最小且比他大,且要从1开始
{
min_sum = INT_MAX;
min_delta = input[] - input[];
for (int i = ; i < length - ; i++)
{
if (input[i + ] - input[i] < min_delta)
{
min_delta = input[i + ] - input[i];
min_sum = min(min_sum, cal(i + , i));
}
else if (input[i + ] - input[i] == min_delta)
min_sum = min(min_sum, cal(i + , i));
}
cout << min_sum << endl;
}
else cal_odd(, length - );//如果cutA!=cutB,那么直接选一头一尾的最小
}
}
}
return ;
} int cal(const int posAS, const int posBS)
{
//专门算偶数重复的情况
int sumA = , sumB = , lenA = , lenB = ;
sumA = input[posAS]; sumB = input[posBS];
for (int i = ; lenA < cutA; i++)
{
if (i != posAS && i != posBS)
{
lenA++;
sumA = sumA * + input[i];
}
}
for (int i = length - ; lenB < cutB; i--)
{
if (i != posAS && i != posBS)
{
++lenB;
sumB = sumB * + input[i];
}
}
return sumA - sumB;
} void cal_odd(const int posAS, const int posBS)
{
int lenA = , lenB = , sumA = , sumB = ;
sumA = input[posAS]; sumB = input[posBS];
for (int i = ; lenA < cutA; i++)
{
if (i != posAS && i != posBS)
{
++lenA;
sumA = sumA * + input[i];
}
}
for (int i = length - ; lenB < cutB; i--)
{
if (i != posAS && i != posBS)
{
++lenB;
sumB = sumB * + input[i];
}
}
cout << sumA - sumB << endl;
}

最后最快的速度,32ms

Enum:Smallest Difference(POJ 2718)的更多相关文章

  1. POJ 2718 Smallest Difference(最小差)

     Smallest Difference(最小差) Time Limit: 1000MS    Memory Limit: 65536K Description - 题目描述 Given a numb ...

  2. Smallest Difference(POJ 2718)

    Smallest Difference Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6740   Accepted: 18 ...

  3. POJ 2718 Smallest Difference(贪心 or next_permutation暴力枚举)

    Smallest Difference Description Given a number of distinct decimal digits, you can form one integer ...

  4. 【POJ - 2718】Smallest Difference(搜索 )

    -->Smallest Difference 直接写中文了 Descriptions: 给定若干位十进制数,你可以通过选择一个非空子集并以某种顺序构建一个数.剩余元素可以用相同规则构建第二个数. ...

  5. poj 2718 Smallest Difference(暴力搜索+STL+DFS)

    Smallest Difference Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6493   Accepted: 17 ...

  6. POJ 2718 Smallest Difference dfs枚举两个数差最小

    Smallest Difference Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 19528   Accepted: 5 ...

  7. LintCode "The Smallest Difference"

    Binary search. class Solution { int _findClosest(vector<int> &A, int v) { , e = A.size() - ...

  8. POJ 2718【permutation】

    POJ 2718 问题描述: 给一串数,求划分后一个子集以某种排列构成一个数,余下数以某种排列构成另一个数,求这两个数最小的差,注意0开头的处理. 超时问题:一开始是得到一个数列的组合之后再从中间进行 ...

  9. The Smallest Difference

    Given two array of integers(the first array is array A, the second array is arrayB), now we are goin ...

随机推荐

  1. 【POJ 2886】Who Gets the Most Candies?

    题意 约瑟夫问题的升级版,每次出去的是前一个出去的人位置+手上的数字(正往前,负往后).第i个出去的人拿的糖是i的约数的个数.求拿糖最多的人和他的糖果数. 分析 线段树单点更新,反素数. 我竟然WA在 ...

  2. cowboy-高性能简洁的erlang版web框架

    那么Cowboy是什么呢? Cowboy is a small, fast and modular HTTP server written in Erlang. 其定位非常明确: Cowboy aim ...

  3. TCP/IP详解 学习六

    从ip层看路由表  选路策略 选路策略:决定把哪些路由放到路由表的规则. Ip执行选路机制,而路由守护程序则提供选路策略. Netstat –rn 打印路由表,如果没有-n命令会搜索配置文件,将网络地 ...

  4. 架构(Architecture)和框架(Framework)杂谈

    1. 架构和框架的设计层次不同       类似于硬件设计,软件设计也分为不同的层次.典型的软件设计层次如下图:        在这个图中我们可以看到,Framework处于Micro-archite ...

  5. 代码重构-4 通用方法 用 static

    只要没有用到 this.变量/方法 的,都可以用static 原代码: private  string GetPeriodDesc(int lotteryPeriod) { return EnumHe ...

  6. js中初学函数的使用

    <script> function SetColor(name,value) { var oDiv=document.getElementById('div3'); oDiv.style[ ...

  7. eclipse 左边目录结构下五referenced library解决办法

    没有referenced library,加入多个jar包,导致项目看上去庞大 解决办法: 在Package Explorer窗口中会出现Referenced Libraries,但Java EE 透 ...

  8. hdu 2044 一只小蜜蜂

    斐波那契数列变形,在本题中不是从1-N,而是从M-N 下标   1   2   3   4   5   6   7     8     9 值     1   1   2   3   5   8   ...

  9. pom.xml

    使用intelJ idea 导入maven包管理文件是,使用Import的方式导入,会自动导入pom.xml来导入包. pom.xml会指定父子关系. 例如,总模块的pom.xml中有一下内容: &l ...

  10. 项目中用到的window.showModalDialog(来自网络)

    window.showModalDialog相关: showModalDialog() (IE 4+ 支持) showModelessDialog() (IE 5+ 支持) window.showMo ...