1. 程式人生 > 實用技巧 >【題解】「P1504」積木城堡

【題解】「P1504」積木城堡

這題是01揹包(\(DP\))

如何判斷要拆走那個積木,首先定義一個\(ans\)陣列,來存放這對積木能拼成多高的,然後如果\(ans_i = n\)那麼就說明這個高度的積木可以。

話不多說,上程式碼!

#include<cstdio> //從最小高度~1列舉, 如果能恰好達到這個高度(即用它有的積木恰好能拼出)有n個城堡
#include<cstring>
#include<algorithm>
using namespace std;

int n, len, min_high = 2e9;
//n表示城堡數,len表示每塊立方體積木的稜長, min_high表示所有城堡初始高度最小值 
int w[10005],ans[10005]; //設ans[i]表示i能被多少組w[1..n]湊成,當dp[i]==true時,ans[i]++
//w[i]表示組成這座城堡的第i塊積木的稜長 
bool dp[10005]; //dp[i]表示能否使用當前的w[1..n]相加得到i
/* 有n件物品(積木),每件物品體積(積木的稜長)為w[i], 價值(積木的稜長)為w[i]。
有容量(城堡高度)為 V 的揹包(城堡)。求在容量(城堡高度)允許的範圍下,揹包裝入物品的價值和(積木的稜長和)有哪些可能值。*/ 


int main()
{
	scanf("%d", &n);
	for(int k = 1; k <= n; k++)
	{
		memset(dp, 0, sizeof(dp));
		int cnt = 1, high = 0; //cnt表示每座城堡含積木的塊數,high表示每座城堡的初始高度 
		while(1)
		{
			scanf("%d", &w[cnt]); //len表示組成這座城堡的每塊積木的稜長
			if(w[cnt] == -1) break;
			high += w[cnt];
			cnt++;
		}
		dp[0] = 1; // dp[0] = 1表示能使用當前的w[1..n]相加得到高度0
		min_high = min(min_high, high); //求出所有城堡初始高度最小值 
        for(int i = 1; i < cnt; i++) //對每座城堡從1~g去列舉每一塊積木 
			for(int j = high; j >= w[i]; j--)
				dp[j] = dp[j] || dp[j-w[i]]; //01揹包變形,即動態轉移方程
		for(int i = high; i >= 1; i--)
        	if(dp[i] == true) ans[i]++; //統計高度i出現次數
	}
    for(int i = min_high; i >= 1; i--) //從最小高度~1列舉
		if(ans[i] == n) //如果能恰好達到這個高度(即用它有的積木恰好能拼出)有n個城堡
		{
			printf("%d\n", i);  
			return 0;
		}
	printf("0\n");
    
	return 0;
}

\(Bye Bye!\)