DP地狱训练 石子归并

 

1254: [DP地狱训练]石子归并

时间限制: 1 Sec  内存限制: 64 MB
提交: 694  解决: 295
[提交][状态][讨论版]

题目描述

有一堆石头质量分别为W1,W2,…,Wn.(Wi≤ 10000),将石头合并为两堆,使两堆质量的差最小。

输入

输入第一行只有一个整数n(1≤n≤50),表示有n堆石子。接下去的n行,为每堆石子质量。

输出

输出只有一行,该行只有一个整数,表示最小的质量差.

样例输入

5 
5
8
13
27
14

样例输出

3

 
 

 
 

判定性dp,见代码。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <cstdlib>
 6 #include <vector>
 7  
 8 using namespace std;
 9  
10 /*
11   f[i][j] = 前i个石子使得|A| - |B| = j是否能够实现
12   f[i][j] = or { f[i - 1][j + w[i]], f[i - 1][j - w[i]] }
13 */
14 #define N (50 * 10000 + 100)
15 int n, w, p;
16 bool f[2][N * 2];
17  
18  
19  
20 int main(){
21     f[0][N + 0] = 1;
22     scanf("%d", &n);
23     for(int i = 1 ; i <= n ; i ++){
24         p = !p;
25         scanf("%d", &w);
26         for(int j = -N ; j <= N ; j ++){
27             f[p][N + j] = f[!p][N + j + w] | f[!p][N + j - w];
28         }
29     }
30     for(int i = 0 ; i <= N ; i ++){
31         if(f[p][N + i] | f[p][N - i]){
32             printf("%d\n", i);
33             break;
34         }
35     }
36 }
View Code

 

转载于:https://www.cnblogs.com/KingSann/articles/7307235.html

在洛谷平台的数字石子合并题中,常涉及不同类型的石子合并,主要使用动态规划(dp)算法求解。对于只能相邻两堆进行合并的情况,可采用区间dp求解。 一个较大的石子堆由两个较小的相邻石子合并形成,即相邻的小区间不断合并成大区间,这符合区间DP的思想。其转移方程是将大的堆拆成两个小的石子堆,枚举中间的点,每次计算合成这两小堆已用代价加上这次合并的代价(这次合并的代价即整个区间的和),找到最小值: ```plaintext for(int k=i;k<j;k++) //k为分割点 dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+a[i] + a[i+1] +...+ a[j] ) ``` 核心代码如下: ```cpp #include <iostream> #include <cstring> using namespace std; const int N = 310; int a[N],prefix[N]; int dp[N][N]; //表示到了区间[i->j]时的最小花费 void solve() { memset(dp,0x3f,sizeof(dp)); int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) prefix[i]=prefix[i-1]+a[i]; //前缀和计算区间和 for(int i=1;i<=n;i++) dp[i][i]=0; //单独合并自己的初始化 //区间DP模板 for(int len=2;len<=n;len++) //枚举分割的区间长度 { for(int i=1;i+len-1<=n;i++) //枚举左端点 { int j=i+len-1; //此时右端点可计算 for(int k=i;k<j;k++) //再对(i->j)进行小区间分割计算,枚举分割点 { //遍历计算最优的分割情况,[i][k]<->[k+1][j]+合并(i->j)的所有石头的权重 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+(prefix[j]-prefix[i-1])); } } } cout<<dp[1][n]<<'\n'; //即输出[1->n]的最小花费 } ``` 此外,对于环形石子合并问题,可使用四边形不等式优化。dp[i][j] 的最大值总是在合并端点处取得,也就是 `max(dp[i+1][j],dp[i][j−1])`。代码如下: ```cpp #include<bits/stdc++.h> #define ll long long using namespace std; int n,w[205],sum[205],dp[205][205],s[205][205]; int inf=0x3fffffff,ans=inf; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>w[i]; sum[i]=sum[i-1]+w[i]; s[i][i]=i; } for(int i=n+1;i<=2*n;i++) { w[i]=w[i-n]; s[i][i]=i; sum[i]=sum[i-1]+w[i]; } auto getvalue=[&](int l,int r) { return sum[r]-sum[l-1]; }; for(int i=1;i<=2*n;i++) { dp[i][i]=0; } for(int i=2*n-1;i>=1;i--) for(int j=i+1;j<=2*n;j++) dp[i][j]=min(dp[i+1][j],dp[i][j-1])+getvalue(i,j); for(int i=1;i<=n;i++) ans=min(ans,dp[i][i+n-1]); cout<<ans<<endl; for(int i=2*n-1;i>=1;i--) for(int j=i+1;j<=2*n;j++) dp[i][j]=max(dp[i+1][j],dp[i][j-1])+getvalue(i,j); ans=-1; for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]); cout<<ans; return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值