Cpp環境【Vijos1037】【CQYZos1391】搭建雙塔

【問題描述】  

  2001年9月11日,一場突發的災難將紐約世界貿易中心大廈夷爲平地,Mr. F曾親眼目睹了這次災難。爲了紀念“9·11”事件,Mr. F決定自己用水晶來搭建一座雙塔。
  Mr. F有N塊水晶,每塊水晶有一個高度,他想用這N塊水晶搭建兩座有同樣高度的塔,使他們成爲一座雙塔,Mr. F可以從這N塊水晶中任取M(1≤M≤N)塊來搭建。但是他不知道能否使兩座塔有同樣的高度,也不知道如果能搭建成一座雙塔,這座雙塔的最大高度是多少。所以他來請你幫忙。
  給定水晶的數量N和每塊水晶的高度Hi,你的任務是判斷Mr. F能否用這些水晶搭建成一座雙塔(兩座塔有同樣的高度),如果能,則輸出所能搭建的雙塔的最大高度,否則輸出“Impossible”。

【輸入格式】  

  第一行爲一個數N,表示水晶的數量。
  第二行爲N個數,第i個數表示第i個水晶的高度。

【輸出格式】  

  輸出僅包含一行,如果能搭成一座雙塔,則輸出雙塔的最大高度,否則輸出一個字符串“Impossible”。

【輸入樣例】  

5
1 3 4 5 2

【輸出樣例】  

7

【數據範圍】  

50%的數據:1≤N≤20, N塊水晶高度的總和不超過2000;
70%的數據:1≤N≤100, N塊水晶高度的總和不超過2000;
100%的數據:1≤N≤100, N塊水晶高度的總和不超過500000。

【來源】

Vijos原題1037 原題傳送矩陣    重慶一中題庫 原題傳送矩陣

【思路梳理】

  首先考慮的應該還是暴力回溯算法,枚舉第i塊水晶放在左塔/放在右塔/不用來搭建,依次枚舉每一塊,對於此題而言有50分,如果不好實現動態規劃也是非常可觀的分數,時間複雜度爲這裏寫圖片描述
  
  接下來是動態規劃程序。
70分程序:
  設狀態函數f(i,j,k)=能否將前i塊水晶搭成左塔高度爲j,右塔高度爲k的雙塔,能爲1,否爲0。
  狀態轉移方程:f(i,j)=f(i-1,j,k)||f(i-1,j-a[i],k)||f(i-1,j,k-a[i])
  爲什麼只能得到70分呢?儘管時間複雜度遠小於這裏寫圖片描述(因爲使用的是前綴和tot[i],除了最後一組數據,tot[i]都甚小於sum,但空間複雜度太高,當高度之和等於500000的時候,即使使用d數組使用bool類型也是肯定會超內存的(500000*500000*1≈238000MB)。
  所以狀態函數要設爲差值,詳見【Cpp代碼】。

【Cpp代碼】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 105
#define maxm 5000010
#define move 500005
using namespace std;
int n,a[maxn],d[2][2*maxm+5];
int tot[maxn];//前綴和 

void dp()
{
    //f(i,j)=將前i塊水晶搭成左塔減右塔高度爲j時左塔的高度 
    //f(i,j)=max(f(i-1,j),f(i-1,j-a[i])+a[i],f(i-1,j+a[i])) 
    memset(d,-1,sizeof(d));
    d[0][0+move]=0;

    for(int i=1;i<=n;i++)
    for(int j=tot[i];j>=-tot[i];j--)
    {
        int t1=d[(i-1)%2][j+move];//不適用第i塊水晶 
        int t2=-1,t3=-1;
        if(d[(i-1)%2][j-a[i]+move]!=-1) t2=d[(i-1)%2][j-a[i]+move]+a[i];
        if(d[(i-1)%2][j+a[i]+move]!=-1) t3=d[(i-1)%2][j+a[i]+move];
        t2=max(t2,t3);
        d[i%2][j+move]=max(t1,t2);
    }

    if(d[n%2][0+move])      printf("%d",d[n%2][0+move]);
    else printf("Impossible");
}

int main()
{
//  freopen("tower.in","r",stdin);
    //freopen("tower.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]),tot[i]=tot[i-1]+a[i];

    dp();
    return 0;
}
發佈了59 篇原創文章 · 獲贊 10 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章