題目:
有n個正整數數,將他們按十進制按某種順序組成一個更大的整數,求最小的這個整數。
舉例,有數字32和321,連接成32132後最小。
又舉例,有數字1876,98,21,4,211連接成1876 211 21 4 98是最小的。
解決思路:
1.用揹包動態規劃的思想很容易。因爲ABCD最小的必要條件ABC最小,時間複雜度是0(n*n*k)
2.第二種解決方法要先做一個數學證明。
首先,規定一種比較規則:a->b表示ab<ba成立,記作規則一。
其次:需要證明一個定律。
定律一:n個正整數的最小組合
X1X2X3....Xn中,Xi->Xj(其中i<j)一定成立。
證明這個定律我們可以用反證法。
假設最小的組合中存在Xj->Xi(其中i<j),即XiXj>XjXi那麼要證明假設不成立我們只需要證明Xi...Xj不是最小的即可。
可以分兩種情況:
第一種:j=i+1。很顯然,XjXi比XiXj更小,這種情況假設不成立。
第二種:j>i+1。即Xi Xi+1...Xj-1 Xj。爲了方便,我們把中間的記作y,把Xi記作a,把Xj記作b。也就是所,我們必須證明ayb不是最小的。
假如ayb是最小的,那肯定存在ay<ya和yb<by。設a的位數是m,y的位數是n,b的位數是k。那麼
1.ay<ya ==> a*10^n+y<y*10^m+a ==> a*(10^n-1)/(10^m-1) < y
2.yb<by ==> y*10^k+b<b*10^n+y ==> y<b*(10^n-1)/(10^k-1)
3.綜合1和2可以得出a*(10^k-1)<b*(10^m-1) ==> a*10^k+b < b*10^m+a ==> ab < ba
即XiXj<XjXi,很顯然與條件矛盾,所以假設不成立。
綜上所述,假設不成立,原命題成真。
有了定律一,其實我們還可以得到另一個定律。
定律二:當ab<ba且bc<cb時,ac<ca一定成立。
這裏,我們有三個正整數,a,b,c。其中a->b且b->c。
因爲我們可以得到個最小的組合。
x1x2x3,且x1->x2,x2->x3且x1->x3。加入b是x1,那麼a就沒得選了,如果b是x3,那麼c就沒得選了。所以b肯定是x2,同理,a必須是x1,c必須是x3。即如下:
abc
故a->c成立。即命題成立。
至此,我們有了一下結論:
給定的n個正整數,我們可以通過規則一將其排序,排序後將它們連起來就是最小組合了。時間複雜度0(k*n*logn)
這裏,我爲什麼要囉嗦地強調定律二呢?因爲沒有定律二,我們就沒辦法按照規則一進行排序。
這裏有一片文章講述了定律一的證明,這裏採用的就是這篇文章的方法。
http://blog.csdn.net/cxllyg/article/details/7659525
這篇文章的問題就是開篇就講按照規則一排序,這是有一個問題,那就是在不知道定律二之前,根本就不知道規則一能不能排序!
另外有一篇文章有提到定律二,但說得太草率了。
http://blog.csdn.net/yysdsyl/article/details/4248537
如果A+B>=B+A,B+C>=C+B,則一定有:A+C>=C+A
誰能告訴我這個定律是怎麼證明的嗎?事實上,我花了很久的時間都沒法直接證明這個定律!
當然,感謝上面兩位大牛,在他們的基礎上,我纔算完美地解決了這個問題。
C++代碼:
//
// MinConnect.h
// test
//
// Created by JiangHuifu on 14-6-1.
// Copyright (c) 2014年 veger. All rights reserved.
//
#ifndef __test__MinConnect__
#define __test__MinConnect__
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class CMinConnect {
vector<char*> datas;//所有的整數以字符串的方式存儲
private:
void getData();//獲取數據,並將數字以十進制字符串的方式存儲起來
void sort();//排序
string getResult();//獲得結果
public:
virtual ~CMinConnect();
void play();//運行算法
static bool cmp( char* &a, char* &b);//比較函數,如果ab<=ba則返回true,否則返回false
};
#endif /* defined(__test__MinConnect__) */
//
// MinConnect.cpp
// test
//
// Created by JiangHuifu on 14-6-1.
// Copyright (c) 2014年 veger. All rights reserved.
//
#include "MinConnect.h"
#include <sstream>
static inline unsigned int log10(unsigned int t){
int i = 1;
do{
t/=10;
i*=10;
}while (t>0);
return i/10;
}
void CMinConnect::play(){
getData();//獲取數據,並將數字以十進制字符串的方式存儲起來
sort();//排序
cout<<getResult();//輸出結果
}
void CMinConnect::getData(){
unsigned int k;
unsigned int n = 1000;
for (unsigned int i = 0; i<n; i++) {
k = abs(rand()%1000000+1);
cout<<k<<endl;
char* p = (char*)malloc(sizeof(char)*(1+log10(k)*10));
if (p==NULL) {
throw -1;
}else{
sprintf(p, "%d",k);
}
datas.push_back(p);
}
}
void CMinConnect::sort(){
std::__1::sort(datas.begin(), datas.end(), CMinConnect::cmp);
}
bool CMinConnect::cmp(char* &a, char* &b){//比較ab和ba大小的函數
if (a==NULL || b==NULL) {//有必要,防止出錯
return true;
}
char *p1=a,*p2=b;
int flag = 3;
while (flag > 0) {
while (('\0' != *p1)&&('\0' != *p2)) {
if (*p1 > *p2) {
return false;
}else if(*p1 < *p2){
return true;
}
++p1;
++p2;
}
if (NULL == p1 && NULL == p2) {
flag -= 2;
}else{
flag -= 1;
if (NULL == p1) {
p1 = b;
}
if (NULL == p2) {
p2 = a;
}
}
}
return true;
}
string CMinConnect::getResult(){
ostringstream s;
s<<"result is:";
for (vector<char*>::iterator i = datas.begin(); i!=datas.end(); ++i) {
s<<" "<<*i;
}
s<<endl;
return s.str();
}
CMinConnect::~CMinConnect(){
for (vector<char*>::iterator i = datas.begin(); i!=datas.end(); ++i) {
free(*i);
*i = NULL;
}
datas.clear();
}