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

    https://www.luogu.com.cn/problem/P1043

    首先,如果学会了这篇题解的思路,那么你应该还可以做对这道题目

    好了,进入正题。

    对于题目中的环形,很容易想到破环成链进行处理(当然,对于初次接触'环形'应该比较难想,这里说容易想是建立在接触过类似的题目(如'能量项链'等)前提上。)破环成链的通法是开一个两倍长的数组然后枚举起点,这题这么写同样没错,我一开始也是这么写的。但是我觉得如果这样写,代码太复杂了,好像要套四层循环,比较难看。我们注意到,本题的链长度很小( 1 <= n <= 50),于是我就开了一个二维数组,相当于把每一条链都分开存。我想这么写应该更好理解。

    此题破环成链后,接下来要处理的是,如何对于一条链求最优解?只要我们知道了求一条链的最优解的方法,那么本题的答案无非就是对每一条链的最优解再求最优解。

    因此,我们定义一个函数用来对每一条链进行dp,然后将每条链的dp结果求最优解。

    这个dp函数怎么写呢?显然要用到区间动归。我们这样定义dp数组:

    dp[ Maxsize ][ Maxsize ]     dp[ i ][ j ] 表示从 1 号元素到 i 号元素, 选取 j 个分界点划分成 ( j + 1)段的最优解。

    状态转移方程 :    dp[ i ][ j ]  =  max( dp[ i ][ j ],  dp[ k ][ j-1 ]  *  val( k+1, i) 。 其中, k 为一个循环枚举的变量,它的取值范围为 [ j, i ) , 它表示分成上一个分界点的标号。  val( k+1,i) 是一个我们自己定义的函数,用来求数列的 k+1 号元素 到 i 号元素的累加和模10。

    代码:

     1 #include <iostream>
     2 #define INF 0x3fffffff
     3 #define MAX(a,b) (a>b?a:b) // 比STL自带的快很多!
     4 #define MIN(a,b) (a<b?a:b)
     5 #define Maxsize 50+5
     6 int arr[Maxsize][Maxsize];
     7 using namespace std;
     8 int val(int front,int back,int arr[]){
     9     int ans = 0;
    10     for (int i = front; i <= back; i++) {
    11         ans += arr[i];
    12     }
    13     ans = ans % 10;
    14     return (ans + 10) % 10;
    15 }
    16 void fun(int arr[],const int n,const int m,int& max_ans,int& min_ans){
    17     int dp[Maxsize][Maxsize]; // 前 i 个数,分成 j 段的答案
    18     for (int i = 0; i < Maxsize; i++) {
    19         for (int j = 0; j < Maxsize; j++) {
    20             dp[i][j] = 0;
    21         }
    22     }
    23     for (int i = 1; i <= n; i++) { // 初始化,分成0段
    24         dp[i][0] = ((dp[i-1][0] + arr[i]) % 10 + 10) % 10;
    25     }
    26     for (int i = 1; i <= n; i++) { // 前 i 个数
    27         for (int j = 1; j <= m; j++) { // 分出j个分界点,分界点归并到左半段
    28             if(i <= j)continue;
    29             for (int k = j; k < i; k++) { // 上一个分界点所对应的位置
    30                 dp[i][j] = MAX(dp[i][j],dp[k][j-1]*val(k+1,i,arr));
    31             }
    32         }
    33     }
    34     max_ans = MAX(max_ans,dp[n][m]);
    35 
    36     for (int i = 0; i < Maxsize; i++) {
    37         for (int j = 0; j < Maxsize; j++) {
    38             dp[i][j] = INF;
    39         }
    40     }
    41     for (int i = 1; i <= n; i++) {
    42         if(i==1) dp[i][0] = (arr[i] % 10 + 10) % 10;
    43         else dp[i][0] = ((dp[i-1][0] + arr[i]) % 10 + 10) % 10;
    44     }
    45 
    46     for (int i = 1; i <= n; i++) { // 前 i 个数
    47         for (int j = 1; j <= m; j++) { // 分出j个分界点,分界点归并到左半段
    48             if(i <= j)continue;
    49             for (int k = j; k < i; k++) { // 上一个分界点所对应的位置
    50                 dp[i][j] = MIN(dp[i][j],dp[k][j-1]*val(k+1,i,arr));
    51             }
    52         }
    53     }
    54     min_ans = MIN(min_ans,dp[n][m]);
    55 }
    56 int main(){
    57     int n,m;
    58     cin >> n >> m; // 分成 m 个部分, 那么就找m-1个分界点
    59     m--;
    60     for (int i = 1; i <= n; i++) {
    61         cin >> arr[1][i]; // 以第一个输入的元素为首元素
    62     }
    63     for (int i = 2; i <= n; i++) {
    64         for (int j = 1; j <= n; j++) {
    65             arr[i][j] = arr[i-1][j+1];
    66         }
    67         arr[i][n] = arr[i-1][1];
    68     }
    69     int max_ans = -INF,min_ans = INF;
    70     for (int i = 1; i <= n; i++) {
    71         fun(arr[i],n,m,max_ans,min_ans);
    72     }
    73     cout << min_ans << endl << max_ans;
    74     return 0;
    75 }

     

     

    ---- suffer now and live the rest of your life as a champion ----
  • 相关阅读:
    比较汇编指令 LEA 和 MOV(转载)
    整数的所有不同分割数目非递归算法
    解决vim终端下的错位问题
    busybox源码剖析(2)pwd.c
    寻找脚码2013年2月24日
    busybox源码剖析(3)cat.c
    找出没有相邻的1的二进制数的个数2013年2月17日
    iOS6开发关于集合视图UICollectionView的相关文章:
    iOS 6 开发应用集合视图(UICollectionView)使用xib文件创建集合视图单元格
    iOS6开发应用集合视图(UICollectionView)创建基于Storyboard的集合视图应用程序
  • 原文地址:https://www.cnblogs.com/popodynasty/p/12506566.html
Copyright © 2011-2022 走看看