石子合并问题

经典石子合并问题

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.环形石子堆,每次取相邻两个堆合并,合并价值为两堆重量(价值)之和:

#include<bits/stdc++.h>
using namespace std;
#define inf 99999999
#define maxn 210
int dp1[maxn][maxn],dp2[maxn][maxn];
int sum[maxn];
int n;
int main(){
    while(cin>>n){
        memset(dp1,0,sizeof dp1);
        sum[0]=0;
        for(int i=1;i<=n;i++){
            int x;
            cin>>x;
            sum[i]=sum[i-1]+x;
        }
        for(int i=n+1;i<=2*n;i++)//将长度增加一倍,首尾相连
            sum[i]=sum[n]+sum[i-n];
        for(int len=1;len<n;len++){//枚举长度
            for(int i=1;i<2*n;i++){//枚举起点,长度为2*n
                int j=len+i;//算出终点
                if(j>2*n)break;//超界就直接跳出循环
                dp1[i][j]=inf;//取小需要初始化最大
                for(int k=i;k<j;k++){
                    dp1[i][j]=min(dp1[i][j],dp1[i][k]+dp1[k+1][j]+sum[j]-sum[i-1]);//求小
                    dp2[i][j]=max(dp2[i][j],dp2[i][k]+dp2[k+1][j]+sum[j]-sum[i-1]);//求大
                }
            }
        }
        int maxx=-1,minn=inf;
        for(int i=1;i<=n;i++){//筛n-n+1区间的值
            maxx=max(dp2[i][i+n-1],maxx);
            minn=min(dp1[i][i+n-1],minn);
        }
        cout<<minn<<endl<<maxx<<endl;
    }
    return 0;
}
View Code

 

posted @ 2019-10-01 19:53  杯酒朝阳  阅读(445)  评论(0编辑  收藏  举报