DP vijos1037 搭建雙塔

2021-07-16 05:41:03 字數 3061 閱讀 5879

【問題描述】

2023年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」。

【輸入樣例】

51 3 4 5 2

【輸出樣例】7

【資料範圍】

50%的資料:1≤n≤20, n塊水晶高度的總和不超過2000;

70%的資料:1≤n≤100, n塊水晶高度的總和不超過2000;

100%的資料:1≤n≤100, n塊水晶高度的總和不超過500000。

題目大意:給你n個水晶的高度h[i],現在求能否將這些水晶分成三組,且其中一組和另一組的h[i]之和相等。

演算法一:暴力搜素o(3^n) 預期得分40

題目要求選n個水晶,對於每乙個水晶,都有三種選擇:

1.將這個水晶加入第一組,第一組的總高度增加h[i];(也就是把水晶放在第乙個塔上)

2.加入第二組。類似1

3.加入第三組(不放)

設函式run(int i,int j,int k)表示當前考慮第i個水晶的放置情況,其中此時第乙個塔高j,第二個塔高k。搜素。

**如下:

#include#include#include#include#includeusing namespace std;

const int maxn=105;

int n,h[maxn];

int ans=0;

void run(int i,int j,int k)

run(i+1,j,k);//不放

run(i+1,j+h[i],k);//放塔一

run(i+1,j,k+h[i]);//放塔二

}int main()

演算法二:遞推演算法o(n*(tot/2)^2) 預期得分65~70

受暴力演算法的啟發,設f(i,j,k)表示能否用前i塊水晶搭出乙個高度為j,另乙個高度為k的塔,能為1,否則為0

f(i,j,k)=f(i-1,j,k)(不選)||f(i-1.j-h[i],k)(選的第一座塔)||f(i-1,j,k-h[i])(選的第二座塔)

然後查詢為1的f[n][i][i]。

#include#include#include#include#includeusing namespace std;

const int maxn=105;

int n,h[maxn];

bool f[10000][10000];/*注意這樣是一種錯誤設法 正確的陣列應該是f[tot/2][tot/2] 但是顯然對於本題的規模記憶體會超128m。所以70分演算法在不超過128m的前提下盡量設大了陣列*/

/* f(j,k)=在前i個水晶中選擇一些,能否搭成一座塔高度為j,另一座塔高度為k,能1 不能0

f(j,k)=f(j,k) || f(j-h[i],k) || f(j,k-h[i])

f(0,0)=1

*/int main()

f[0][0]=1;

for(int i=1;i<=n;i++)

for(int j=tot/2;j>=0;j--)

for(int k=tot/2;k>=0;k--)

int ans=0;

for(int i=1;i<=tot/2;i++)if(f[i][i])

if(ans<=0)printf("impossible\n");

else printf("%d\n",ans);

return 0;

}

演算法三:動態規劃 o(n*2*tot)預期得分100

演算法二的主要問題在於空間和時間複雜度還是太高,所以優化從這兩方面著手。

空間上重新考慮狀態函式f(i,j,k),思考能否從空間上化三維為二維,再用滾動陣列優化。

這時候設f(i,j)表示從前i個水晶中選擇一些,搭成的兩座塔的高度差為j時,塔1的最高高度(假設塔1始終比塔2高)。

考慮第i塊水晶的情況,第i塊水晶可以不放t1=f(i-1,j),放塔1上t2=f(i-1,j-h[i])+h[i],放塔2上t3=f(i-1,j+h[i])

所以f(i,j)=max

注意實現時可能出現負數下標,所以要平移下標(巨集定義平移)。還有為了節約空間,一定要用滾動陣列。

同時時間上少了一重循壞,時間問題也得到了解決。

演算法三**:

#include#include#include#include#includeusing namespace std;

const int maxn=105;

const int inf=500010;

int n,h[maxn],sum[maxn];

int d[2][2*1000020];

#define f(i,j) d[(i)%2][(j)+500000]

/* f(i,j)=在前i塊水晶中選擇一些,使得塔1和塔2的高度差為j時塔1的最大高度(塔1比塔2矮)

f(i,j)=max

f(0,0)=0 其餘為-inf

*/void ready()

int main() }

if(f(n,0)>0)printf("%d\n",f(n,0));

else printf("impossible\n");

return 0;

}

VIJOS P1037 搭建雙塔

2001年9月11日,一場突發的災難將紐約世界 中心大廈夷為平地,mr.f曾親眼目睹了這次災難。為了紀念 9?11 事件,mr.f決定自己用水晶來搭建一座雙塔。mr.f有n塊水晶,每塊水晶有乙個高度,他想用這n塊水晶搭建兩座有同樣高度的塔,使他們成為一座雙塔,mr.f可以從這n塊水晶中任取m 1 m...

Vijos P1037 搭建雙塔

p1037搭建雙塔 accepted 2001年9月11日,一場突發的災難將紐約世界 中心大廈夷為平地,mr.f曾親眼目睹了這次災難。為了紀念 9?11 事件,mr.f決定自己用水晶來搭建一座雙塔。mr.f有n塊水晶,每塊水晶有乙個高度,他想用這n塊水晶搭建兩座有同樣高度的塔,使他們成為一座雙塔,m...

搭建雙塔(Vijos 1037)

2001年9月11日,一場突發的災難將紐約世界 中心大廈夷為平地,mr.f曾親眼目睹了這次災難。為了紀念 9?11 事件,mr.f決定自己用水晶來搭建一座雙塔。mr.f有n塊水晶,每塊水晶有乙個高度,他想用這n塊水晶搭建兩座有同樣高度的塔,使他們成為一座雙塔,mr.f可以從這n塊水晶中任取m 1 m...