经典石子合并问题
1.每次取任意两个堆合并,合并价值为两堆重量(价值)之和:
贪心,每次取最小的两堆(哈夫曼模型),优先队列可以直接写
2.每次取相邻两个堆合并,合并价值为两堆重量(价值)之和:
堆数很小的时候(堆数<3000大概):区间dp+平行四边形优化
#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f #define maxn 300 int sum[maxn],n; int Minval(){ int dp[maxn][maxn],s[maxn][maxn]; for(int i=1;i<=n;i++){ dp[i][i]=0; s[i][i]=i; } for(int len=1;len<n;len++){ for(int i=1;i<=n-len;i++){ int j=i+len; dp[i][j]=inf; for(int k=s[i][j-1];k<=s[i+1][j];k++){//平行四边形优化 if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]<dp[i][j]){ dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]; s[i][j]=k;//i-j标记是从k开始的 } } } } return dp[1][n]; } int main(){ while(cin>>n){ sum[0]=0; for(int i=1;i<=n;i++){ int x; cin>>x; sum[i]=sum[i-1]+x; } cout<<Minval()<<endl; } return 0; }View Code
堆数很大的时候:GarsiaWachs算法优化
#include<bits/stdc++.h> #define LL long long using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 40005; int stone[maxn], n, ans, t; void combine(int k); int main() { scanf("%d", &n); stone[0] = inf; stone[n+1] = inf-1; for(int i = 1; i <= n; ++i) scanf("%d", &stone[i]); ans = 0; t = 3; for(int i = 3; i <= n+1; ++i) { stone[t++] = stone[i]; while(stone[t-3] <= stone[t-1]) combine(t-2); } while(t > 3) combine(t-1); printf("%d\n", ans); return 0; } void combine(int k) { int tmp = stone[k-1]+stone[k]; ans += tmp; --t; for(int i = k; i < t; ++i) stone[i] = stone[i+1]; int j; for(j = k-1; stone[j-1] < tmp; --j) stone[j] = stone[j-1]; stone[j] = tmp; while(j >= 2 && stone[j] >= stone[j-2]) { int d = t-j; combine(j-1); j = t-d; } }View Code
3.每次相邻l-r个堆合并,合并价值为合并堆的总重量(价值):
#include<bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f int stone[105]; int dp[105][105][105]; int sum[105]; int main() { int n,l,r; while(~scanf("%d%d%d",&n,&l,&r)){ memset(sum,0,sizeof(sum)); memset(dp,INF,sizeof(dp)); for(int i =1;i<=n;i++){ scanf("%d",&stone[i]); sum[i] = sum[i - 1] + stone[i];//重量 dp[i][i][1] = 0; } for(int len = 2;len<=n;len++){//枚举长度 for(int j = 1;j+len-1<=n;j++){//枚举起点,ends<=n for(int k=2;k<=len;k++){ for(int p=j;p<=j+len-2;p++){ dp[j][len+j-1][k]=min(dp[j][len+j-1][k],dp[j][p][k-1]+dp[p+1][j+len-1][1]); } if(k>=l&&k<=r)dp[j][j+len-1][1]=min(dp[j][j+len-1][1],dp[j][j+len-1][k]+sum[j+len-1]-sum[j-1]); } } } if(dp[1][n][1]==INF)cout<<"0"<<endl; else cout<<dp[1][n][1]<<endl; } return 0; }View Code
4.环形石子堆,每次取相邻两个堆合并,合并价值为两堆重量(价值)之和: