其他分享
首页 > 其他分享> > Luogu-P1880 [NOI1995]石子合并

Luogu-P1880 [NOI1995]石子合并

作者:互联网

题目

题目链接

 

 

测试得分:  100

 

 

主要算法 :  动态规划,区间DP(环状DP)

 

 

 

题干:

    环状区间DP板子题

 

 

应试策略:

  1. 再看你一眼,发现你是区间DP
  2. 手动将数组环状向前旋转一个单位,实现环形区间DP要求(说实话,这个方法有点low)
  3. 确定区间DP操作,需要确定区间两端,又因为要避免重复计算,从后方开始(是这样的吧?( • ̀ω•́ )✧)
  4. 再设置一个过渡位置,在区间两端之间(包括左端点),进行DP状态方程转移f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+1][j]+s[j]-s[i-1]),f2[i][j]=max(f2[i][j],f2[i][k]+f2[k+1][j]+s[j]-s[i-1]); 
  5. 因为需要输出最大值与最小值,所以需要进行两种操作
  6. 最后再每个N单位的区间操作中筛选出答案

  

   代码

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FORa(i,s,e) for(int i=s;i<=e;i++)
#define FORs(i,s,e) for(int i=s;i>=e;i--)

using namespace std;

const int N=100;
int n,min_ans=214748364,max_ans,a[N+1],s[N+1],f1[N+1][N+1],f2[N+1][N+1];//f1[i][j]为i-j这区间合并在一起的最小值,f2[i][j]为i-j这区间合并在一起的最大值
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
int main()
{
    scanf("%d",&n);
    FORa(i,1,n) scanf("%d",&a[i]);
    FORa(k,1,n)
    {
        int p=a[1];
        FORa(i,1,n) a[i-1]=a[i];//将链进行更新操作 
        a[n]=p,s[0]=0;
        FORa(i,1,n) s[i]=s[i-1]+a[i];//s[]为前缀和数组,优化常数
        memset(f1,127/3,sizeof(f1)); 
        memset(f2,0,sizeof(f2));//每一次都需要赋初值 
        FORa(i,1,n) f1[i][i]=0;//初状态的确定 
        FORs(i,n-1,1)
            FORa(j,i+1,n)
                FORa(k,i,j-1)
                    f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+1][j]+s[j]-s[i-1]),f2[i][j]=max(f2[i][j],f2[i][k]+f2[k+1][j]+s[j]-s[i-1]); //状态转移方程 
        min_ans=min(min_ans,f1[1][n]),max_ans=max(max_ans,f2[1][n]);//筛选答案 
    }
    printf("%d\n%d",min_ans,max_ans);
    return 0;
}

 

 

 

 

优化策略:

 

 

  代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FORa(i,s,e) for(int i=s;i<=e;i++)
#define FORs(i,s,e) for(int i=s;i>=e;i--)

using namespace std;

const int N=100;
int n,min_ans=214748364,max_ans,a[2*N+2],s[2*N+2],f1[2*N+2][2*N+2],f2[2*N+2][2*N+2];//f1[i][j]为i-j这区间合并在一起的最小值,f2[i][j]为i-j这区间合并在一起的最大值 
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
int main()
{
    scanf("%d",&n);
    FORa(i,1,n) scanf("%d",&a[i]),a[i+n]=a[i],s[i]=s[i-1]+a[i];//化环为链, 
    FORa(i,n+1,2*n) s[i]=s[i-1]+a[i];//s[]为前缀和数组,优化常数
    memset(f1,127/3,sizeof(f1));
    FORa(i,1,2*n) f1[i][i]=0;
    FORs(i,2*n-1,1)
        for(int j=i+1;j<=2*n&&j-i<=n;j++)
            FORa(k,i,j-1)
                f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+1][j]+s[j]-s[i-1]),f2[i][j]=max(f2[i][j],f2[i][k]+f2[k+1][j]+s[j]-s[i-1]);//状态转移方程 
    FORa(i,1,n) min_ans=min(min_ans,f1[i][i+n-1]),max_ans=max(max_ans,f2[i][i+n-1]);//筛选答案 
    printf("%d\n%d",min_ans,max_ans);
    return 0;
}

 

 

 

总结:

  化繁为简,确定DP本身的性质

 

 

 

 

 

 

 

标签:f1,f2,int,Luogu,FORa,ans,P1880,NOI1995,区间
来源: https://www.cnblogs.com/SeanOcean/p/11208890.html