最小的差别

  题目大意:输入一串数字,问你数字的组合方式,你可以随意用这些数字组合(无重复)来组合成一些整数(第一位不能为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. Oracle写函数读写日志实例

    1.用DBA登录赋权限create or replace directory D_OUTPUT as 'D:\TEMP'; grant read,write on directory D_OUTPUT ...

  2. BZOJ-1880 Elaxia的路线 SPFA+枚举

    1880: [Sdoi2009]Elaxia的路线 Time Limit: 4 Sec Memory Limit: 64 MB Submit: 921 Solved: 354 [Submit][Sta ...

  3. Android学习笔记01-Mac下搭建Java开发环境

    一 安装JDK 下载 mac 下专用的jdk1.7, 下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downlo ...

  4. POJ2392Space Elevator(贪心+背包)

    Space Elevator Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9970   Accepted: 4738 De ...

  5. Looper

    /** * Class used to run a message loop for a thread. Threads by default do * not have a message loop ...

  6. DEEP LEARNING WITH STRUCTURE

    DEEP LEARNING WITH STRUCTURE Charlie Tang is a PhD student in the Machine Learning group at the Univ ...

  7. --hdu 2570 迷瘴(贪心)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2570 Ac code: #include<stdio.h> #include<std ...

  8. Visual Studio Online Integrations

                                                                                                         ...

  9. Android Studio 设置不自动缩进匿名内部类

    Android Studio 会默认缩进匿名内部类代码,这让人感觉有些不大适应,可以使用下面的方法进行取消. 取消选中橙色框前的几个复选框即可.

  10. 保存vim的ide环境

    开发周期不是一两天, 要把当前的窗口布局, 命令历史/寄存器历史等保存下来,以便下次编写时快速恢复. 需要保存两个方面的信息: session: 保存窗口的view试图窗口布局, 和全局设置   :m ...