题目描述
XC 的儿子小 XC 最喜欢玩的游戏用积木垒漂亮的城堡。城堡是用一些立方体的积木垒成的,城堡的每一层是一块积木。
小 XC 是一个比他爸爸 XC 还聪明的孩子,他发现垒城堡的时候,如果下面的积木比上面的积木大,那么城堡便不容易倒。所以他在垒城堡的时候总是遵循这样的规则。
小 XC 想把自己垒的城堡送给幼儿园里漂亮的女孩子们,这样可以增加他的好感度。为了公平起见,他决定把送给每个女孩子一样高的城堡,这样可以避免女孩子们为了获得更漂亮的城堡而引起争执。
可是他发现自己在垒城堡的时候并没有预先考虑到这一点。所以他现在要改造城堡。由于他没有多余的积木了,他灵机一动,想出了一个巧妙的改造方案。他决定从每一个城堡中挪去一些积木,使得最终每座城堡都一样高。为了使他的城堡更雄伟,他觉得应该使最后的城堡都尽可能的高。
任务:
请你帮助小 XC 编一个程序,根据他垒的所有城堡的信息,决定应该移去哪些积木才能获得最佳的效果。
注意:一座城堡的高度,是组成它的所有积木的棱长之和。
输入格式
第一行是一个整数 nn,表示一共有 nn 座城堡。
以下 nn 行每行是一系列非负整数,用一个空格分隔,按从下往上的顺序依次给出一座城堡中所有积木的棱长。用 -1
结束。
输出格式
一个整数,表示最后城堡的最大可能的高度。
如果找不到合适的方案,则输出 00。
输入输出样例
输入 #1复制
2 2 1 -1 3 2 1 -1
输出 #1复制
3
说明/提示
【数据范围】
对于 100\%100% 的数据,1 \le n \le 1001≤n≤100,一座城堡中的积木不超过 100100 块,每块积木的棱长不超过 100100
这道题可以用DPDP(动态规划)来做~
DP必备三步:
- 状态表示状态表示
- 动态转移动态转移
- 初始状态初始状态
开始吧!~~~~~ 我们可以先只考虑单独城堡~k~ k ~~
First~~状态表示First 状态表示
- dp[i][j]~dp[i][j] 表示用前~i~ i 块积木能否达到高度~j~ j
Next ~~动态转移Next 动态转移
-
不选第~i~ i 块积木 dp[i][j] = dp[i-1][j]dp[i][j]=dp[i−1][j]
-
选第~i~ i 块积木 dp[i][j] = dp[i-1][j-w[i]]dp[i][j]=dp[i−1][j−w[i]]
倒叙降维,用~|=~ ∣= 连接~~ 得:
- dp[j] ~~|= ~~dp[j-w[i]]dp[j] ∣= dp[j−w[i]]
Last ~~初始状态Last 初始状态
很简单~
不搭积木肯定行呀!
- dp[0] = 1dp[0]=1
用代码呈现~
#include <bits/stdc++.h> //万能头
using namespace std;
int n,len,min_high = 2e9,w[105],ans[10005];
// w数组为每一城堡积木的高度 ans数组为达到高度i的个数
bool dp[10005];
// Dp嘛
int main() {
cin >> n; //n个城堡
for(int k=1;k<=n;k++)// 每一个城堡
{
//memset(w,0,sizeof(w)); // 其实不用
memset(dp,0,sizeof(dp)); // 以免前一个影响当前城堡
int cnt = 0,high = 0; //cnt为积木个数,high为高度~
while(1)
{
cin >> len; //输入len
if(len == -1) break; // 输入完了~
w[++cnt] = len; // 存高度~
high += len; // 算高度~
}
// 开始DP!
dp[0] = 1; // 初始状态
for(int i=1;i<=cnt;i++)// 物品维(cnt个积木)
for(int j=high;j>=w[i];j--)// 容量~倒叙枚举压物品维 枚举到物品大小
dp[j] |= dp[j-w[i]]; //动态转移方程
min_high = min(min_high,high); // 最小值~
for(int i = high;i>=1;i--) // 检查一下能否达到高度
if(dp[i] == 1) ans[i]++;// 加到ans数组~
}
for(int i = min_high;i>=1;i--)// 看看那个高度可以所有达到!
if(ans[i] == n)
{
cout << i;// 输出
return 0;
}
cout << "0";//没有,输出0
return 0;
}
PS:代码两重循环,看范围,不会TLETLE的~