zoukankan      html  css  js  c++  java
  • 动态规划——区间

    Wikioi 1048 石子归并

    题目描述 Description

    有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。

    输入描述 Input Description

    第一行一个整数n(n<=100)

    第二行n个整数w1,w2...wn  (wi <= 100)

    输出描述 Output Description

    一个整数表示最小合并代价

    样例输入 Sample Input

    4

    4 1 1 4

    样例输出 Sample Output

    18

    思路:

    以区间长度划分阶段

    ps.如果是任意两堆直接贪心,越是先合并的计算次数就越多

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 #include<algorithm>
     6 
     7 using namespace std;
     8 const int maxn = 200,maxnum = 10000000;
     9 int n,j,value[maxn],sum[maxn],dp[maxn][maxn];
    10 int main(){
    11     cin>>n;
    12     for(int i = 1;i <= n;i++){
    13         cin>>value[i];
    14         sum[i]= sum[i-1] + value[i];
    15     }
    16     for(int l = 2;l <= n;l++){
    17         for(int i = 1;i <= n - l + 1;i++){
    18             j = i + l - 1;
    19             dp[i][j] = maxnum;
    20             for(int k = i;k < j;k++)
    21                dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]);
    22         }
    23     }
    24     cout<<dp[1][n];
    25     return 0;
    26 }
    View Code

    Wikioi 3002 石子归并3

    题目描述 Description

    有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。

    输入描述 Input Description

    第一行一个整数n(n<=3000)

    第二行n个整数w1,w2...wn  (wi <= 3000)

    输出描述 Output Description

    一个整数表示最小合并代价

    样例输入 Sample Input

    4

    4 1 1 4

    样例输出 Sample Output

    18

    数据范围及提示 Data Size & Hint

    数据范围相比“石子归并” 扩大了

    思路:

    四边形不等式,记录区间划分点K

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 #include<algorithm>
     6 
     7 using namespace std;
     8 const int maxn = 4000,maxnum = 100000000;
     9 int n,j,value[maxn],sum[maxn],dp[maxn][maxn],a[maxn][maxn];
    10 int main(){
    11     cin>>n;
    12     for(int i = 1;i <= n;i++){
    13         cin>>value[i];
    14         sum[i]= sum[i-1] + value[i];
    15         a[i][i] = i;
    16     }
    17     for(int l = 2;l <= n;l++){
    18         for(int i = 1;i <= n - l + 1;i++){
    19             j = i + l - 1;
    20             dp[i][j] = maxnum;
    21             for(int k = a[i][j-1];k <= a[i+1][j];k++){
    22                 if(dp[i][j] > dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]){
    23                     dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
    24                     a[i][j] = k;
    25                 }
    26                 
    27             }
    28                
    29         }
    30     }
    31     cout<<dp[1][n];
    32     return 0;
    33 }
    View Code

    Wikioi 2102 石子归并2

    题目描述 Description

    在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
    试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

    输入描述 Input Description

    数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

    输出描述 Output Description

    输出共2行,第1行为最小得分,第2行为最大得分.

    样例输入 Sample Input

    4
    4 4 5 9

    样例输出 Sample Output

    43
    54

    数据范围及提示 Data Size & Hint

    经典的区间动态规划。

    思路:
    求最小值的过程与求最大值的过程基本一样
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 #include<algorithm>
     6 
     7 using namespace std;
     8 const int maxn = 500,maxnum = 100000000;
     9 int n,sum[maxn],value[maxn],dp[maxn][maxn],dps[maxn][maxn];
    10 int main(){
    11     cin>>n;
    12     for(int i = 1;i <= n;i++){
    13         cin>>value[i];
    14         value[i+n] = value[i];
    15         sum[i] = sum[i-1] + value[i];
    16         
    17     }
    18     for(int i = 1;i <= n;i++) sum[i+n] = sum[i] + sum[n];
    19     for(int j = 2;j <= n + n;j++){
    20         for(int i = j - 1;i >= 1 && j - i + 1 <= n;i--){
    21             dp[i][j] = maxnum;
    22             for(int k = i;k < j;k++){
    23                 dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]); 
    24             }
    25             for(int k = i;k < j;k++){
    26                 dps[i][j] = max(dps[i][j],dps[i][k] + dps[k+1][j] + sum[j] - sum[i-1]); 
    27             }
    28         }
    29     }
    30     int ans = maxnum;
    31     for(int i = 1;i <= n;i++) ans = min(ans,dp[i][i+n-1]);
    32     cout<<ans<<endl;
    33     ans = 0;
    34     for(int i = 1;i <= n;i++) ans = max(ans,dps[i][i+n-1]);
    35     cout<<ans;
    36     return 0;
    37 }
    View Code
    Wikioi 3657 括号序列
    题目描述 Description

    我们用以下规则定义一个合法的括号序列:

    (1)空序列是合法的

    (2)假如S是一个合法的序列,则 (S) 和[S]都是合法的

    (3)假如A 和 B 都是合法的,那么AB和BA也是合法的

    例如以下是合法的括号序列:

    ()[](())([])()[]()[()]

    以下是不合法括号序列的:

    ([])(([])([()

     现在给定一些由'(', ')', '[', ,']'构成的序列 ,请添加尽量少的括号,得到一个合法的括号序列。

    输入描述 Input Description

    输入包括号序列S。含最多100个字符(四种字符: '(', ')', '[' and ']') ,都放在一行,中间没有其他多余字符。

    输出描述 Output Description

    使括号序列S成为合法序列需要添加最少的括号数量。

    样例输入 Sample Input

       

    ([()
    样例输出 Sample Output

       

    2
    数据范围及提示 Data Size & Hint

       

    【样例说明】
    最少添加2个括号可以得到合法的序列:()[()]或([()])
    【数据范围】
    S的长度<=100 (最多100个字符)。

     思路:

    长度划分,状态左右端点,dp[i][j] = dp[i+1][j-1] + 1(if i matches to j)

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 #include<algorithm>
     6 
     7 using namespace std;
     8 const int maxn = 200,maxnum = 20000000;
     9 int n,c,value[maxn],dp[maxn][maxn],sign,j,q;
    10 char cmd;
    11 string s;
    12 int main(){
    13     n = c = 0;
    14     getline(cin,s);
    15     q = s.length();
    16     for(int i = 1;i <= q;i++){
    17         cmd = s[i-1];
    18         if(cmd == '(') {value[i-c] = 1;n++;}
    19         if(cmd == '[') {value[i-c] = 2;n++;}
    20         if(cmd == ']') {value[i-c] = 3;n++;}
    21         if(cmd == ')') {value[i-c] = 4;n++;}
    22     }
    23     memset(dp,0,sizeof(dp));
    24     for(int i = 1;i <= n;i++) dp[i][i] = 1;
    25     for(int l = 2;l <= n;l++){
    26         for(int i = 1;i <= n -l + 1;i++){
    27             j = i + l - 1;
    28             dp[i][j] = maxnum; 
    29             if(value[i] + value[j] == 5 && value[i] < value[j]) dp[i][j] = dp[i+1][j-1];
    30             for(int k = i;k < j;k++){
    31                 dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j]);
    32             }
    33             
    34         }
    35     }
    36     cout<<dp[1][n];
    37     return 0;
    38 }
    View Code

     Wikioi 1154 能量项链

    题目描述 Description

    在Mars星球上,每个Mars人都随身佩带着一串能量项链。在项链上有N颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m*r*n(Mars单位),新产生的珠子的头标记为m,尾标记为n。

    需要时,Mars人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。

    例如:设N=4,4颗珠子的头标记与尾标记依次为(2,3) (3,5) (5,10) (10,2)。我们用记号⊕表示两颗珠子的聚合操作,(j⊕k)表示第j,k两颗珠子聚合后所释放的能量。则第4、1两颗珠子聚合后释放的能量为:

    (4⊕1)=10*2*3=60。

    这一串项链可以得到最优值的一个聚合顺序所释放的总能量为

    ((4⊕1)⊕2)⊕3)=10*2*3+10*3*5+10*5*10=710。

    输入描述 Input Description

    第一行是一个正整数N(4≤N≤100),表示项链上珠子的个数。第二行是N个用空格隔开的正整数,所有的数均不超过1000。第i个数为第i颗珠子的头标记(1≤i≤N),当i<N< span>时,第i颗珠子的尾标记应该等于第i+1颗珠子的头标记。第N颗珠子的尾标记应该等于第1颗珠子的头标记。

    至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序。

    输出描述 Output Description

    只有一行,是一个正整数E(E≤2.1*109),为一个最优聚合顺序所释放的总能量。

    样例输入 Sample Input

    4

    2 3 5 10

    样例输出 Sample Output

    710

    注意保存方式

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 #include<algorithm>
     6 
     7 using namespace std;
     8 const int maxn = 500,maxnum = 220000000;
     9 int n,sum[maxn],value[maxn],dp[maxn][maxn];
    10 int main(){
    11     cin>>n;
    12     for(int i = 1;i <= n;i++){
    13         cin>>value[i];
    14         value[i+n] = value[i];
    15         
    16     }
    17     for(int j = 2;j <= n + n;j++){
    18         for(int i = j - 1;i >= 1 && j - i  <= n;i--){
    19             for(int k = i;k < j;k++){
    20                 dp[i][j] = max(dp[i][j],dp[i][k] + dp[k+1][j] + value[i] * value[j+1] * value[k+1]);
    21             }
    22         }
    23     }
    24     int ans = 0;
    25     for(int i = 1;i <= n;i++) ans = max(ans,dp[i][i+n-1]);
    26     cout<<ans;
    27     return 0;
    28 }
    View Code
  • 相关阅读:
    用MobaXterm_Personal_12.0远程连接ubuntu虚拟机,并解决报错connection refused
    二叉树图解以及二叉树的递归原理
    Source Insight 4.0 安装使用简单粗暴教程
    VScode配置嵌入式linux开发环境,代替source insight
    linux系统下部署DNS正向解析
    【剑指Offer】面试题47. 礼物的最大价值
    【LeetCode】1371. 每个元音包含偶数次的最长子字符串
    【LeetCode】680. 验证回文字符串 Ⅱ
    【剑指Offer】面试题46. 把数字翻译成字符串
    【剑指Offer】面试题45. 把数组排成最小的数
  • 原文地址:https://www.cnblogs.com/hyfer/p/4842013.html
Copyright © 2011-2022 走看看