zoukankan      html  css  js  c++  java
  • 区间dp②

    区间dp的第②篇,稍微有点难度

    关路灯

    题目描述

    某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。

    为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。

    现在已知老张走的速度为1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位:m)、功率(W),老张关灯所用的时间很短而可以忽略不计。

    请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。

    输入输出格式

    输入格式:

    文件第一行是两个数字n(1<=n<=50,表示路灯的总数)和c(1<=c<=n老张所处位置的路灯号);

    接下来n行,每行两个数据,表示第1盏到第n盏路灯的位置和功率。数据保证路灯位置单调递增。

    输出格式:

    一个数据,即最少的功耗(单位:J,1J=1W·s)。

    输入输出样例

    输入样例:       
     5 3
    2 10
    3 20
    5 20
    6 30
    8 10
    输出样例:
    270 
    都是一段区间,因为你走过的路的灯都会顺手关掉;所以考虑区间dp

    先按顺序把灯排序,然后考虑dp[i][j]表示关掉区间[i,j]里所有灯的最小花费,但好像没法转移,应为可能我关完[i,j]里的
    灯后我在区间左端点,然后我要往右走去关掉右区间的灯,或者我在右端点,所以我不好表示走过的路程,于是可以加①维表示在
    左端点还是右端点
    dp[i][j][0/1]表示关掉区间里的灯在左/右端点的最小花费
    然后考虑转移
    dp[i][j][0]<-dp[i+1][j][0/1]
    dp[i][j][1]<-dp[i][j-1][0/1]
    然后花式转移方程,详见代码
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    
    int min(int a,int b)
    {
        return a<b?a:b;
    }
    int n,c;
    int wz[60],pw[60],tot[60];
    int dp[60][60][2];
        
    int main()
    {
        scanf("%d%d",&n,&c);
        memset(dp,120,sizeof(dp));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&wz[i],&pw[i]);    
            tot[i]=tot[i-1]+pw[i];    //tot前缀和数组,记录tot[i]记录从1到i的耗电量
        }
        dp[c][c][1]=0;         //初始化出生点
        dp[c][c][0]=0;
        for(int k=2;k<=n;k++)    //区间大小
        {
            for(int i=1;i+k-1<=n;i++)      ///区间左右端点
            {
                int j=i+k-1;
                dp[i][j][0]=min(dp[i+1][j][0]+(wz[i+1]-wz[i])*(tot[i]+tot[n]-tot[j]),      //从区间[i+1][j]向[i][j]宽展,距离*耗电量(脚动模拟下标)
                                dp[i+1][j][1]+(wz[j]-wz[i]) * (tot[i]+tot[n]-tot[j]));
                dp[i][j][1]=min(dp[i][j-1][0]+(wz[j]-wz[i]) * (tot[i-1]+tot[n]-tot[j-1]),
                                dp[i][j-1][1]+(wz[j]-wz[j-1])*(tot[i-1]+tot[n]-tot[j-1]));
            }
        }
        printf("%d",min(dp[1][n][0],dp[1][n][1]));
        
    }

    感觉这题还好,细心处理下表ok

    然后看下道题

    棋盘分割
    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 15794   Accepted: 5609

    Description

    将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)

    原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
    均方差,其中平均值,xi为第i块矩形棋盘的总分。
    请编程对给出的棋盘及n,求出O'的最小值。

    Input

    第1行为一个整数n(1 < n < 15)。
    第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。

    Output

    仅一个数,为O'(四舍五入精确到小数点后三位)。

    Sample Input

    3
    1 1 1 1 1 1 1 3
    1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 0
    1 1 1 1 1 1 0 3

    Sample Output

    1.633
    脚动推公式,可知要是分的每个区间的平方和最小(简单的数学)

    然后发现数据很小,可以考虑dp[k][x1][y1][x2][y2]表示左上角[x1][y1]右上角[x2][y2]分了k个的时候的平方和
    然后可以发现他可以横切,也可以竖切,然后选切得其中一块;于是可以尝试写方程:
                                                            少女祈祷中......
    dp[i][x][y][x1][y1]=min(dp[i][x][y][x1][y1],
            min(dp[i-1][x][y][x1][k]+dp[0][x][k+1][x1][y1],dp[0][x][y][x1][k]+dp[i-1][x][k+1][x1][y1]));
    dp[i][x][y][x1][y1]=min(dp[i][x][y][x1][y1],
         
     min(dp[i-1][x][y][k][y1]+dp[0][k+1][y][x1][y1],dp[0][x][y][k][y1]+dp[i-1][k+1][y][x1][y1]));

    看着方程很长很乱 很油很亮很duang 其实没什么软用
    仔细分析,红字和蓝字表示竖着切拿右边还是左边,橙字和绿字表示横着切拿下面还是上面;
    然后就是一些垃圾的dp前处理
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    int dp[15][9][9][9][9];
    int sum[9][9];
    int n;int tot;
    int main()
    {
    
        scanf("%d",&n);
        for(int i=1;i<=8;++i)
        {
            for(int j=1;j<=8;++j)
            {
                int a;
                scanf("%d",&a);
                sum[i][j]=sum[i][j-1]+a;
                tot+=a;
            }
        }
        for(int i=2;i<=8;++i)
            for(int j=1;j<=8;++j)
            sum[i][j]+=sum[i-1][j];        //sum[i][j]表示以1,1到i,j的矩形大小
        for(int x=1;x<=8;++x)
            for(int y=1;y<=8;++y)
                for(int x1=1;x1<=8;++x1)
                    for(int y1=1;y1<=8;++y1) 
                     dp[0][x][y][x1][y1]=
                     (sum[x1][y1]-sum[x-1][y1]-sum[x1][y-1]+sum[x-1][y-1])
                     *(sum[x1][y1]-sum[x-1][y1]-sum[x1][y-1]+sum[x-1][y-1]);      //每个格子的值赋为自己的平方
        for(int i=1;i<n;++i)
        {
            for(int x=1;x<=8;++x)
            {
                for(int y=1;y<=8;++y)
                {
                    for(int x1=x;x1<=8;++x1)
                    {
                        for(int y1=y;y1<=8;++y1)
                        {
                            dp[i][x][y][x1][y1]=0x3f3f3f3f;
                            for(int k=y;k<y1;++k) dp[i][x][y][x1][y1]=min(dp[i][x][y][x1][y1],                  //刚才的方程
                            min(dp[i-1][x][y][x1][k]+dp[0][x][k+1][x1][y1],dp[0][x][y][x1][k]+dp[i-1][x][k+1][x1][y1]));
                            for(int k=x;k<x1;++k) dp[i][x][y][x1][y1]=min(dp[i][x][y][x1][y1],
                            min(dp[i-1][x][y][k][y1]+dp[0][k+1][y][x1][y1],dp[0][x][y][k][y1]+dp[i-1][k+1][y][x1][y1]));
                        }
                    }
                }
            }
        }
        double ans=sqrt(dp[n-1][1][1][8][8]*1.0/n-((tot*1.0)/n*(tot*1.0)/n));        //脚动解出的式子
        printf("%0.3lf",ans);
    }
        
    
    

    然后一定要多做题

     
  • 相关阅读:
    Changing Icon File Of Push Button At Runtime In Oracle Forms 6i
    Set Font Properties On Mouse Hover Of Push Button And Text Items At Run time In Oracle Forms
    Change An Item Property Using Set_Item_Property In Oracle Forms
    Calling / Running a report in Oracle forms 10g / 11g
    Change Or Set Report Object Property At Run Time In Oracle Forms Using Set_Report_Object_Property Command
    Refresh / Updating a form screen in Oracle D2k Forms 6i
    Know How And When To Use System.Message_Level To Control Messages In Oracle Forms
    Perform Cut Copy Paste Operations Using Cut_Region Copy_Region Paste_Region Commands In Oracle Forms
    CHECKBOX_CHECKED built-in in Oracle D2k Forms
    Limiting To Select Only 5 Check Boxes Out Of Ten In Oracle Forms
  • 原文地址:https://www.cnblogs.com/AidenPearce/p/8467998.html
Copyright © 2011-2022 走看看