有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
2014-07-08 10:14
369 查看
第一种解法:
[cpp] view
plaincopyprint?
/*
*copyright@nciaebupt 转载请注明出处
*问题:有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
*要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
*比如 a=[100 ,99 ,98 ,1 ,2 ,3]; b=[1, 2, 3, 4, 5, 40];结果为48
*求解思路:
*当前数组a和数组b的和之差为
*A = sum(a) - sum(b)
*a的第i个元素和b的第j个元素交换后,a和b的和之差为
*A' = sum(a) - a[i] + b[j] - (sum(b) - b[j] + a[i])
* = sum(a) - sum(b) - 2 (a[i] - b[j])
* = A - 2 (a[i] - b[j])
*设x = a[i] - b[j]
*所以 A' = A-2x
*假设A > 0,
*当x 在 (0,A)之间时,做这样的交换才能使得交换后的a和b的和之差变小,
*x越接近A/2效果越好,
*如果找不到在(0,A)之间的x,则当前的a和b就是答案。
*所以算法大概如下:
*在a和b中寻找使得x在(0,A)之间并且最接近A/2的i和j,交换相应的i和j元素,
*重新计算A后,重复前面的步骤直至找不到(0,A)之间的x为止。
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int sum(int a[],int len)
{
int res = 0;
for(int i=0;i<len;++i)
{
res = res + a[i];
}
return res;
}
void swap(int * a,int * b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
bool isXinRangeA(int x,int A)
{
if((x < A && x > 0) ||(x > A && x < 0))
return true;
return false;
}
void exchangeAB(int *a, int *b,int len)
{
int A = sum(a,len) - sum(b,len);
double min = a[0]-b[0]-A/2.0;
int ii = 0;
int jj = 0;
int flag = 0;
if(A == 0)
return ;
for(int i = 0;i < len;++i)
{
for(int j = 0;j < len;++j)
{
int x = a[i] - b[j];
if( x == 0)
continue;
if(isXinRangeA(x,A))
{
double tmp = x - A/2.0;
if(tmp < min)
{
min = tmp;
flag = 1;
ii = i;
jj = j;
cout<<"***"<<endl;
}
}
}
}
if(flag == 1)
{
swap(&a[ii],&b[jj]);
exchangeAB(a,b,len);
}
}
int main(int args,char ** argv)
{
//int a[] = {100 ,99 ,98 ,1 ,2 ,3};
//int b[] = {1, 2, 3, 4, 5, 40};
int a[] = {-3,9,10,65};
int b[] = {5,6,13,55};
int len = sizeof(a)/sizeof(int);
exchangeAB(a,b,len);
//打印数组A
for(int i = 0;i < len;++i)
{
cout<<a[i]<<" ";
}
cout<<endl;
//打印数组B
for(int j = 0;j < len;++j)
{
cout<<b[j]<<" ";
}
cout<<endl;
//打印数组A的和与数组B的和的差值
cout<<abs(sum(a,len)-sum(b,len))<<endl;
system("pause");
return 0;
}
以上这种解法是有缺陷的,得到的结果并不一定是全局最优值,因为:
一,题目要求的是差值最小的方案,所以交换一对数据是不能实现的。
二,最后交换一对数据无法使差值减小,但是存在同时交换两对(还有更多对)数据可以减小差值的可能。
比如:如果两个序列分别是[-3,9,10,65]和[5,6,13,55],按以上的算法这就是最优解了。可是显然[-3,5,13,65]和[6,9,10,55]更好。
所以下面给出一种能找出全局最优值的解法,使用动态规划的算法实现:
[cpp] view
plaincopyprint?
/*
*copyright@nciaebupt 转载请注明出处
*问题:有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
*要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
*比如 a=[100 ,99 ,98 ,1 ,2 ,3]; b=[1, 2, 3, 4, 5, 40];结果为48
*求解思路:使用动态规划的思路
* 外阶段:在前i个数中进行选择,i=1,2...2*n。
* 内阶段:从这i个数中任意选出j个数,j=1,2...i。
* 状态:这j个数的和为s,s=1,2...sum/2。
* 决策:决定这j个数的和有两种决策,一个是这j个数中包含第i个数,另一个是不包含第i个数。
* dp[k][s]表示从前k个数中取任意个数,且这些数之和为s的取法是否存在。
*在程序中我们给出S(k)的所有可能取值v和arr[k],去寻找v-arr[k]是否在S(k-1)={Vi}中,
*由于S(k)的可能取值的集合的大小与k无关,
*所以这样设计的动态规划算法其第k步的时间复杂度与k无关
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
#define MAXN 101
#define MAXSUM 100000
bool dp[MAXN][MAXSUM];
int c_sum(int *c,int len)
{
int sum = 0;
for(int i = 0;i < len;++i)
{
sum = sum + c[i];
}
return sum;
}
int min(int a,int b)
{
if(a < b)
return a;
else
return b;
}
void exchangeAB(int * c,int len)
{
int sum = c_sum(c,2*len);
memset(dp,0,sizeof(dp));
dp[0][0] = true;
//外阶段i表示第i个数,内阶段j表示选取数的个数
for(int i = 1;i <= 2*len;++i)//外阶段i
{
for(int j = min(i,len);j >=1;--j)//内阶段j
{
for(int s = 1;s <= sum/2;++s)//S(k)的所有可能取值s
{
if((s >= c[i]) && dp[j-1][s-c[i]])//j个数中是否包含第i个数
{
dp[j][s]=true;
//cout<<s<<endl;
}
}
}
}
int s = sum/2;
for(;s>=1 && !dp[len][s];s--)
;
cout<<sum - 2*s<<endl;
}
int main(int args,char ** argv)
{
//int a[] = {100 ,99 ,98 ,1 ,2 ,3};
//int b[] = {1, 2, 3, 4, 5, 40};
int a[] = {-3,9,10,65};
int b[] = {5,6,13,55};
int len = sizeof(a)/sizeof(int);
//将数组a与b中的值放在数组c中
int c[MAXN];
int pos = 0;
for(int i = 0;i <= len;++i)
{
c[i] = a[i];
pos = i;
}
for(int j = 0;j <= len;++j)
{
c[pos+j] = b[j];
}
for(int i = 0;i < 2*len;++i)
{
cout<<c[i]<<" ";
}
cout<<endl;
exchangeAB(c,len);
system("pause");
return 0;
}
注意:如果数组中有负数的话,上面的背包策略就不能使用了(因为第三重循环中的s是作为数组的下标的,不能出现负数的),需要将数组中的所有数组都加上最小的那个负数的绝对值,将数组中的元素全部都增加一定的范围,全部转化为正数,然后再使用上面的背包策略就可以解决了。
[cpp] view
plaincopyprint?
/*
*copyright@nciaebupt 转载请注明出处
*问题:有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
*要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
*比如 a=[100 ,99 ,98 ,1 ,2 ,3]; b=[1, 2, 3, 4, 5, 40];结果为48
*求解思路:
*当前数组a和数组b的和之差为
*A = sum(a) - sum(b)
*a的第i个元素和b的第j个元素交换后,a和b的和之差为
*A' = sum(a) - a[i] + b[j] - (sum(b) - b[j] + a[i])
* = sum(a) - sum(b) - 2 (a[i] - b[j])
* = A - 2 (a[i] - b[j])
*设x = a[i] - b[j]
*所以 A' = A-2x
*假设A > 0,
*当x 在 (0,A)之间时,做这样的交换才能使得交换后的a和b的和之差变小,
*x越接近A/2效果越好,
*如果找不到在(0,A)之间的x,则当前的a和b就是答案。
*所以算法大概如下:
*在a和b中寻找使得x在(0,A)之间并且最接近A/2的i和j,交换相应的i和j元素,
*重新计算A后,重复前面的步骤直至找不到(0,A)之间的x为止。
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int sum(int a[],int len)
{
int res = 0;
for(int i=0;i<len;++i)
{
res = res + a[i];
}
return res;
}
void swap(int * a,int * b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
bool isXinRangeA(int x,int A)
{
if((x < A && x > 0) ||(x > A && x < 0))
return true;
return false;
}
void exchangeAB(int *a, int *b,int len)
{
int A = sum(a,len) - sum(b,len);
double min = a[0]-b[0]-A/2.0;
int ii = 0;
int jj = 0;
int flag = 0;
if(A == 0)
return ;
for(int i = 0;i < len;++i)
{
for(int j = 0;j < len;++j)
{
int x = a[i] - b[j];
if( x == 0)
continue;
if(isXinRangeA(x,A))
{
double tmp = x - A/2.0;
if(tmp < min)
{
min = tmp;
flag = 1;
ii = i;
jj = j;
cout<<"***"<<endl;
}
}
}
}
if(flag == 1)
{
swap(&a[ii],&b[jj]);
exchangeAB(a,b,len);
}
}
int main(int args,char ** argv)
{
//int a[] = {100 ,99 ,98 ,1 ,2 ,3};
//int b[] = {1, 2, 3, 4, 5, 40};
int a[] = {-3,9,10,65};
int b[] = {5,6,13,55};
int len = sizeof(a)/sizeof(int);
exchangeAB(a,b,len);
//打印数组A
for(int i = 0;i < len;++i)
{
cout<<a[i]<<" ";
}
cout<<endl;
//打印数组B
for(int j = 0;j < len;++j)
{
cout<<b[j]<<" ";
}
cout<<endl;
//打印数组A的和与数组B的和的差值
cout<<abs(sum(a,len)-sum(b,len))<<endl;
system("pause");
return 0;
}
以上这种解法是有缺陷的,得到的结果并不一定是全局最优值,因为:
一,题目要求的是差值最小的方案,所以交换一对数据是不能实现的。
二,最后交换一对数据无法使差值减小,但是存在同时交换两对(还有更多对)数据可以减小差值的可能。
比如:如果两个序列分别是[-3,9,10,65]和[5,6,13,55],按以上的算法这就是最优解了。可是显然[-3,5,13,65]和[6,9,10,55]更好。
所以下面给出一种能找出全局最优值的解法,使用动态规划的算法实现:
[cpp] view
plaincopyprint?
/*
*copyright@nciaebupt 转载请注明出处
*问题:有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
*要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
*比如 a=[100 ,99 ,98 ,1 ,2 ,3]; b=[1, 2, 3, 4, 5, 40];结果为48
*求解思路:使用动态规划的思路
* 外阶段:在前i个数中进行选择,i=1,2...2*n。
* 内阶段:从这i个数中任意选出j个数,j=1,2...i。
* 状态:这j个数的和为s,s=1,2...sum/2。
* 决策:决定这j个数的和有两种决策,一个是这j个数中包含第i个数,另一个是不包含第i个数。
* dp[k][s]表示从前k个数中取任意个数,且这些数之和为s的取法是否存在。
*在程序中我们给出S(k)的所有可能取值v和arr[k],去寻找v-arr[k]是否在S(k-1)={Vi}中,
*由于S(k)的可能取值的集合的大小与k无关,
*所以这样设计的动态规划算法其第k步的时间复杂度与k无关
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
#define MAXN 101
#define MAXSUM 100000
bool dp[MAXN][MAXSUM];
int c_sum(int *c,int len)
{
int sum = 0;
for(int i = 0;i < len;++i)
{
sum = sum + c[i];
}
return sum;
}
int min(int a,int b)
{
if(a < b)
return a;
else
return b;
}
void exchangeAB(int * c,int len)
{
int sum = c_sum(c,2*len);
memset(dp,0,sizeof(dp));
dp[0][0] = true;
//外阶段i表示第i个数,内阶段j表示选取数的个数
for(int i = 1;i <= 2*len;++i)//外阶段i
{
for(int j = min(i,len);j >=1;--j)//内阶段j
{
for(int s = 1;s <= sum/2;++s)//S(k)的所有可能取值s
{
if((s >= c[i]) && dp[j-1][s-c[i]])//j个数中是否包含第i个数
{
dp[j][s]=true;
//cout<<s<<endl;
}
}
}
}
int s = sum/2;
for(;s>=1 && !dp[len][s];s--)
;
cout<<sum - 2*s<<endl;
}
int main(int args,char ** argv)
{
//int a[] = {100 ,99 ,98 ,1 ,2 ,3};
//int b[] = {1, 2, 3, 4, 5, 40};
int a[] = {-3,9,10,65};
int b[] = {5,6,13,55};
int len = sizeof(a)/sizeof(int);
//将数组a与b中的值放在数组c中
int c[MAXN];
int pos = 0;
for(int i = 0;i <= len;++i)
{
c[i] = a[i];
pos = i;
}
for(int j = 0;j <= len;++j)
{
c[pos+j] = b[j];
}
for(int i = 0;i < 2*len;++i)
{
cout<<c[i]<<" ";
}
cout<<endl;
exchangeAB(c,len);
system("pause");
return 0;
}
注意:如果数组中有负数的话,上面的背包策略就不能使用了(因为第三重循环中的s是作为数组的下标的,不能出现负数的),需要将数组中的所有数组都加上最小的那个负数的绝对值,将数组中的元素全部都增加一定的范围,全部转化为正数,然后再使用上面的背包策略就可以解决了。
相关文章推荐
- 《数组-规划》 有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小
- 每天学习一算法系列(29)(有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b 中的元素,使[序列a 元素的和]与[序列b 元素的和]之间的差最小)
- [微软]有两个序列a,b,大小都为n,序列元素的值任意整数,无序; 要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小
- 有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
- 写正确函数需要注意的地方:两个序列a,b,大小都为n,序列元素的值为任意整数,无序;要求通过交换a,b中的元素,使序列a元素的和与序列b元素的和之间的差最小
- 有两个序列a,b,大小都为n,序列元素的值任意整数,无序; 要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
- 有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
- [微软]有两个序列a,b,大小都为n,序列元素的值任意整数,无序; 要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小
- 有两个序列a,b,大小都为 n,序列元素的值任意整数,无序 通过交换a,b中的元素,使序列 a的和与序列b的和之间的差最小
- 有两个序列a,b,大小都有n,序列元素的值任意整数,无序; 要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b无素的和]之间的差最大。
- 华为面试题(8分钟写出代码) 有两个数组a,b,大小都为n,数组元素的值任意,无序; 要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小
- 华为面试题(8分钟写出代码) 有两个数组a,b,大小都为n,数组元素的值任意,无序; 要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小
- 有两个数组a,b,大小都为n,数组元素的值任意,无序;要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小
- 有两个数组a,b,大小都为n,数组元素的值任意,无序;要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小
- 有两个数组a,b,大小都为n,数组元素的值任意,无序; 要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小。
- 有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b中的元素,使[序列a元素的和
- 华为面试题 题目:有两个数组a,b,大小都为n,数组元素的值任意,无序; 要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小
- 【华为】题目:有两个数组a,b,大小都为n,数组元素的值任意,无序; 要求: 通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小。
- 有两个序列a,b,要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
- java编程:两个无序数组a和b,交换a,b中的元素,使得[序列a元素的和]与[序列b元素的和]之间的差最小。