洛谷P1880 石子合并(环形石子合并 区间DP)
题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入输出格式
输入格式:
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:
输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
输出样例#1: 复制
43
54
思路:
就是在普通的石子合并的基础上,改成环形的而已。
转移方程依旧是dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j])dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j])。
解决方法就是
将换拆成链,那么拆成连的过程总要将其长度变为2倍,DP依旧按照原来的DP方案,最主要的变化在于
答案的输出的时候。
原来线性合并的答案在dp[1][n]dp[1][n].
因为在不同地方拆开,所以,要在dp[1][n],dp[2][n+1],dp[3][n]...dp[n−1][2∗n−1]dp[1][n],dp[2][n+1],dp[3][n]...dp[n−1][2∗n−1]中寻找最值,即为答案。
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<set> #include<list> #include<deque> #include<map> #include<time.h> #include<queue> #include <chrono> #include <random> #define ll long long int using namespace std; inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;} int moth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; int dir[4][2]={1,0 ,0,1 ,-1,0 ,0,-1}; int dirs[8][2]={1,0 ,0,1 ,-1,0 ,0,-1, -1,-1 ,-1,1 ,1,-1 ,1,1}; const int inf=0x3f3f3f3f; const ll mod=1e9+7; int dpmin[207][207]; int dpmax[207][207]; int a[207]; int sum[207]; int main(){ ios::sync_with_stdio(false); int n; cin>>n; memset(dpmin,inf,sizeof(dpmin)); for(int i=1;i<=n;i++){ cin>>a[i]; a[i+n]=a[i]; } for(int i=1;i<=2*n;i++){ sum[i]=sum[i-1]+a[i]; dpmin[i][i]=0; } for(int len=2;len<=n;len++){ //枚举长度 for(int i=1;i+len<=2*n;i++){ //枚举起始点 int j=i+len-1; //结束点 //dpmin[i][j]=inf; for(int k=i;k<j;k++){ //枚举分割点 dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]-sum[i-1]+sum[j]); dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]-sum[i-1]+sum[j]); } //cout<<dpmin[i][j]<<endl; } } int ansmin=inf; int ansmax=-inf; for(int i=1;i<=n;i++){ ansmin=min(ansmin,dpmin[i][i+n-1]); ansmax=max(ansmax,dpmax[i][i+n-1]); } cout<<ansmin<<endl<<ansmax<<endl; return 0; }