• 动态规划-石子问题


    1.直线取石子

    #include <stdio.h>
    #include <memory.h>
    #include <math.h>
    #include <string>
    #include <vector>
    #include <set>
    #include <stack>
    #include <queue>
    #include <algorithm>
    #include <map>
    
    
    #define I scanf
    #define OL puts
    #define O printf
    #define F(a,b,c) for(a=b;a<c;a++)
    #define FF(a,b) for(a=0;a<b;a++)
    #define FG(a,b) for(a=b-1;a>=0;a--)
    #define LEN 100
    #define MAX 1<<30
    #define V vector<int>
    
    using namespace std;
    
    const int n=6;
    int a[n]={1,5,6,7,3,2};
    int sum[n];
    int dp[n][n];
    
    int getmin(){
        int i,j,v,k;
        F(v,1,n){
            FF(i,n-v){
                j=i+v;
                int tmp=sum[j]-((i>0)?sum[i-1]:0);
                dp[i][j]=MAX;
                F(k,i,j){
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+tmp);
                }
            }
        }
        return dp[0][n-1];
    }
    
    int main(){
        memset(dp,0,sizeof(dp));
        sum[0]=a[0];
        int i;
        F(i,1,n){
            sum[i]=sum[i-1]+a[i];
        }
        printf("%d
    ",getmin());
        return 0;
    }
    View Code

    i:开始下标

    j:结束下标

    k:区间下标,在【i,j)区间进行循环


    2.圆形取石子

    我按照直线取石子的方法在OJ:https://www.luogu.org/problemnew/show/P1880 中无法AC,然后研读了博客 http://blog.csdn.net/acdreamers/article/details/18039073 的代码:

    博客代码:

    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
      
    using namespace std;  
    const int INF = 1 << 30;  
    const int N = 205;  
      
    int mins[N][N];  
    int maxs[N][N];  
    int sum[N],a[N];  
    int minval,maxval;  
    int n;  
      
    int getsum(int i,int j)  //[i,i+j]
    {                        //[i,n-1]      +   [0, (i+j)%n) ]
        if(i+j >= n) return getsum(i,n-i-1) + getsum(0,(i+j)%n);  
        else return sum[i+j] - (i>0 ? sum[i-1]:0);  
    }  
      
    void Work(int a[],int n)  
    {  
        for(int i=0;i<n;i++)  
            mins[i][0] = maxs[i][0] = 0;  
        for(int j=1;j<n;j++)  
        {  
            for(int i=0;i<n;i++)  
            {  
                int s=i,e=i+j;
                int delta=getsum(i,j);//sum∈[i , i+j ]
                mins[i][j] = INF;  
                maxs[i][j] = 0;  
                for(int k=0;k<j;k++)  //在取石子数目上进行优化
                {  
                    mins[i][j] = min(mins[i][j],
                        //从i开始,取k颗    从i+k+1开始,取j-k-1颗
                        mins[i][k] + mins[(i+k+1)%n][j-k-1] + delta);  
                    maxs[i][j] = max(maxs[i][j],
                        maxs[i][k] + maxs[(i+k+1)%n][j-k-1] + delta);  
                }  
            }  
        }  
        minval = mins[0][n-1];  
        maxval = maxs[0][n-1];  
        for(int i=0;i<n;i++)  
        {  
            minval = min(minval,mins[i][n-1]);  
            maxval = max(maxval,maxs[i][n-1]);  
        }  
    }  
      
    int main()  
    {  
        freopen("D:/CbWorkspace/动态规划/石子合并.txt","r",stdin);
        while(scanf("%d",&n)!=EOF)  
        {  
            for(int i=0;i<n;i++)  
                scanf("%d",&a[i]);  
            sum[0] = a[0];  
            for(int i=1;i<n;i++)  
                sum[i] = sum[i-1] + a[i];  
            Work(a,n);  
            printf("%d %d
    ",minval,maxval);  
        }  
        return 0;  
    }  
    View Code

    循环情况:

    下标含义:

    i:取石子的开始下标【0,n)

    j:取多少颗石子【1,n)

    k:区间下标,在当前去j颗石子之内循环 ,k∈【0,j)

      由k切割为了两个区间:【i,k】和【i+k+1,j-k-1】

      含义为:区间①为从i开始,取了k颗石子。区间②为从区间①结束得地方(i+k+1)取剩下的石子(j-k-1)。

      加起来一共取了j-1颗。

    对于k的实例分析:

    ●j=1时(取1颗),k=0,

      划分为两个区间:①【0,0】,②【1,0】,这两个区间的dp值都是0 。然后加上sum(i,i+j),得到了正确的值

    ●j=2时,k=0~1,

      k=0时,①【0,0】,②【1,1】,用s=i,e=i+j还原:【0,0】,【1,2】

      k=1时,①【0,1】,②【2,0】,     ~      :【0,1】,【2,2】

    注:以上分析都没有用“取模运算”来对下标进行重定位

    理解后我的编码(主代码):

        int i,j,k;
        I("%d",&n);
        F(i,0,n){
            I("%d",&a[i]);
        }
        sum[0] =a[0];
        F(i,1,n) sum[i]=a[i]+sum[i-1];
        F(j,1,n){                                //开始下标 
            F(i,0,n){                            //取石子数目
                int delta=getsum(i,i+j);       //区间[i,i+j]的 和 
                mins[i][j]=MAX;                  //初始化mins 
                F(k,0,j){                        //区间下标 
                    mins[i][j]=min(mins[i][j],
                                   mins[i][k] + mins[(i+k+1)%n][j-k-1]+delta) ;
                    maxs[i][j]=max(maxs[i][j],
                                   maxs[i][k] + maxs[(i+k+1)%n][j-k-1]+delta) ;
                } 
            } 
        }
        //计算完毕,找出最后的最大值和最小值
        F(i,0,n){
            max_ans=max(max_ans,maxs[i][n-1]);
            min_ans=min(min_ans,mins[i][n-1]);
        } 
        O("%d
    %d
    ",min_ans,max_ans);

    完整代码:

    #include <stdio.h>
    #include <memory.h>
    #include <math.h>
    #include <string>
    #include <vector>
    #include <set>
    #include <stack>
    #include <queue>
    #include <algorithm>
    #include <map>
    
    #define I scanf
    #define OL puts
    #define O printf
    #define F(a,b,c) for(a=b;a<c;a++)
    #define FF(a,b) for(a=0;a<b;a++)
    #define FG(a,b) for(a=b-1;a>=0;a--)
    #define LEN 300
    #define MAX 1<<30
    #define V vector<int>
    
    using namespace std;
    
    int a[LEN];
    int sum[LEN];
    int maxs[LEN][LEN];
    int mins[LEN][LEN];
    int max_ans=0;
    int min_ans=MAX;
    int n;
    
    int getsum(int i,int j){
        if(j>=n){
            return getsum(i,n-1) + getsum(0,j%n);
        }else{
            return sum[j] - ( i>0 ? sum[i-1] : 0 );
        }
    }
    
    int main(){
    //    freopen("D:/CbWorkspace/动态规划/石子合并.txt","r",stdin);
        int i,j,k;
        I("%d",&n);
        F(i,0,n){
            I("%d",&a[i]);
        }
        sum[0] =a[0];
        F(i,1,n) sum[i]=a[i]+sum[i-1];
        F(j,1,n){                                //开始下标 
            F(i,0,n){                            //取石子数目
                int delta=getsum(i,i+j);    //区间[i,i+j]的 和 
                mins[i][j]=MAX;                    //初始化mins 
                F(k,0,j){                        //区间下标 
                    mins[i][j]=min(mins[i][j],
                                   mins[i][k] + mins[(i+k+1)%n][j-k-1]+delta) ;
                    maxs[i][j]=max(maxs[i][j],
                                   maxs[i][k] + maxs[(i+k+1)%n][j-k-1]+delta) ;
                } 
            } 
        }
        //计算完毕,找出最后的最大值和最小值
        F(i,0,n){
            max_ans=max(max_ans,maxs[i][n-1]);
            min_ans=min(min_ans,mins[i][n-1]);
        } 
        O("%d
    %d
    ",min_ans,max_ans);
        return 0;
    }
    View Code
  • 相关阅读:
    docker 安装 postgresql10[转] 规格严格
    实现html和word的相互转换(带图片) 规格严格
    JasperReport报表导出踩坑实录 规格严格
    Postman设置环境变量[转] 规格严格
    PDF 图书
    理解 ASP.NET Core:Cookie 认证
    你在错误命名你的测试用例!
    精通 ASP.NET Core MVC (第 7 版) 源码下载
    使用 VS Code 徒手构建 PDF 文件
    OpenType 字体文件组织结构
  • 原文地址:https://www.cnblogs.com/TQCAI/p/8431295.html
Copyright © 2020-2023  润新知