The basis of dynamic programming

Dynamic Programming Dynamic Programming

Methodology

  The nature of the computer is a state machine, all data stored in memory constitute the current state, CPU can only take advantage of the current state of the calculated next state (not to tangle external storage hard disk, even if they would only consider the expansion of the state storage capacity only, does not change under a state can only be calculated from the current state out of this iron rule)
  when you attempt to use a computer to solve a problem is, in fact, thinking about how to put this issue expressed as state (which is stored with which variables data) and how to transfer state (how the variables calculated in accordance with some other variable). So-called space complexity is to support your calculations necessary for the storage of up to state how many of the so-called time complexity is the number of steps required to reach the final intermediate state from the initial state!
  Example: To calculate that the number of non-wave deed, a state of each non-Fibonacci number is this problem, two per state before seeking a new digital needs only. So the same time, saving a maximum of only two states, the spatial complexity is constant; calculated for each time a new state is required to be constant and linearly increasing state, the time complexity is linear. This state calculation is straightforward, just need to follow a fixed pattern to calculate a new state from the old state on the line a[i]=a[i-1]+a[i-2], you do not need to consider whether or not the need for more state, the old state which does not need to choose to calculate the new state. For such a solution, we called recursion.

  • Optimal state of each stage is obtained by the optimal state on a stage of -> greedy;
  • Optimal state of each phase is obtained from the combination phase before all state -> search;

  • Optimal state can be obtained at each stage before and regardless of whether the state is how to get directly from a certain state or a certain stage before -> Dynamic Programming.
    • Optimal state of each stage can be obtained from a certain state or a certain stage directly before this property is called optimal substructure.
    • No matter before this state is how to get this property is called no after-effect.
Why DP will be faster?

  Either DP or violence, our algorithms are possible within the solution space, find the optimal solution. Violence approach is to enumerate all possible solutions, this is the most likely solution space. DP is an enumeration promises to be the answer to the solution of (optimal substructure). This space is much smaller than violence. In other words: DP comes pruning . Give up a lot of the answers can not be the optimal solution. So we can get DP's core idea: try to narrow down the possible solution space. Violence in the algorithm, it may be the size of the solution space is often exponential; if we use the DP, it is possible that the size of the solution space down polynomial. In general, the solution space is smaller, faster to find the solution. This completes the optimization.

How to design algorithms DP

  First, we are faced with a situation expressed as x. This step is called the design state. For the state x, remember we asked the answer (eg minimum cost) is f (x). Our goal is to find f (T). Find f (x) with which the situation concerning (denoted by p), write a formula (referred to as the state transition equation) to launch f (x) by f (p).

Step DP

  DP algorithm design, often three steps:

  • who am I? - design state, indicating the situation
  • I come from?
  • I want to go? - After the transfer of both design

  Is the basis for the design condition DP. The next transfer of design, there are two ways: one is to consider where I came from; the other is to consider me where to go, after obtaining f (x) which is common in, went to update some of the solution from x . This is also a lot of DP, we will encounter later. All in all, "I come," and "I want to go," one only needs to think clearly, will be able to design a state transition equation to write code to solve the problem. The former, also known as pull-type transfer, the latter also known as metastasis push type.

References
mathematical derivation

Template examples

/*
题意:求n个元素的数组的m个连续子段的和的最大值
dp[i][j] = 选择i个连续子段时 前j个数的最大值 
状态转移方程:max[i-1][j-1]=max{ dp[i - 1][k] }(i - 1 <= k <= j - 1)
                dp[i][j] = max{dp[i][j - 1],max[i-1][j-1]} + a[j]
解释:第j个元素可以和前面的连接在一起,构成i段dp[i][j-1]+a[j],
        或者独自成为一段,dp[i-1][k]+a[j] 因为分成i-1段,所以k>=i-1
        前j - 1个数必须组合出i - 1段,选择多种情况里的最大值,
注:最后由于数据较大,所以要压缩空间,使用滚动数组
*/
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
#define ull unsigned long long
#define ll long long
#define MAX 1000005
const int inf = 0x3f3f3f3f;

int t, n, m, x, y, z;
int a[MAX],mx;
int dp[MAX];
int ma[MAX];

int main()
{
    while (scanf("%d%d",&m,&n)!=EOF)
    {
        memset(dp, 0, sizeof(dp));
        memset(ma, 0, sizeof(ma));
        for (int i = 1; i <= n; i++)
            scanf("%d",a+i);
        
        
        for (int i = 1; i <= m; i++)
        {
            mx = -inf;
            for (int j = i; j <= n; j++)
            {
                dp[j] = max(dp[j - 1], ma[j - 1]) + a[j];
                ma[j-1]= mx;                //为下一轮i循环保留max[i-1][j-1]
                mx = max(mx,dp[j]);         //mx为dp[i][i]到dp[i][j]的最大值
            }
        }
        printf("%d\n", mx); //因为循环到n所以mx最终为结果
    }
    return 0;
}
/*
题意:求奇数个元素的数组的出现次数大于一半的数
        此数出现的次数大于其他数出现次数的总和,
状态转移方程:新增数==现有最多次数的数,次数+1,else 次数-1
                次数为0时转移到新增数
解释:每增加一个数做一次决策判断之前的数是否是新增状态中的次数过半的数,
        若判定越过的数当前不可能次数过半,由于一定有次数过半的数,
        因此可以暂定新增数为次数过半的数
注:此题没有卡内存,因此还可以用桶装,输出第一个次数过(n+1)/2的数
    for (int i = 1; i <= n; i++)
    {
        cin >> x;
        a[x]++;
    if (a[x] == (n + 1) / 2)
        y = x;
    }
    cout << y << endl;
*/
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
#define ull unsigned long long
#define ll long long
#define MAX int(1e6+5)
const int inf = 0x3f3f3f3f;

int t, n, m, cnt, x, y, z;
int a[MAX];
int dp[MAX];
int ma[MAX];

int main()
{
    while (cin >> n)
    {
        cnt = 0;
        
        for (int i = 1; i <= n; i++)
        {
            cin >> x;
            if (cnt == 0)
            {
                m = x;
                cnt = 1;
            }
            else
            {
                if (m == x)
                    cnt++;
                else cnt--;
            }
        }
        cout << m << endl;
    }
    return 0;
}
最长上升子序列(LIS)
/*
**题意:求n个元素的数组中最长的由依次增大的元素组成的子序列
**状态转移方程:if (a[i] > a[j])
**              dp[i] = max(dp[i], dp[j] + 1);
**                  sum = max(dp[i], sum);
**解释:整体最长上升子序列的从头开始的每个部分都是特点末元素的最长上升子序列
**      每增加一个元素,判断以此元素是否比以其他元素大
**      若大则以j元素结尾的最长上升子序列dp[j]+1
**      且得到所有以i元素结尾的子序列,比较得以i元素结尾的最长上升子序列
**      一直到第n个元素,得到以n元素结尾的最长上升序列
**      比较所有元素结尾的最大子序列,得到最长上升子序列
**      注:  if (a[i] > a[j])
**                  dp[i] = max(dp[i], dp[j] + a[i]);
**                  sum = max(dp[i], sum);
**      此方程得到最大上升子序列(所有元素的和最大)
*/
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
#define ull unsigned long long
#define ll long long
#define MAX int(1e6+5)
const int inf = 0x3f3f3f3f;

int t, n, m, cnt, sum, flag;
int x, y, z;
int a[MAX];
int dp[MAX];

int main()
{
    while (cin >> n)
    {
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        a[0] = 0;
        sum = 0;
        for(int i=1;i<=n;i++)
            for (int j = 0; j < i; j++)
            {
                if (a[i] > a[j])
                    dp[i] = max(dp[i], dp[j] + 1);

                sum = max(dp[i], sum);
            }
        cout << sum << endl;
    }
    return 0;
}

Exercises core code

  • nlogn find the longest sequence and rising deformation

  • Luo Gu P1439 longest common subsequence template
    general solution: we can use p[i][j]to represent the first before i bit string, LCS length j bits before the second string, then we can easily imagine the state transition equation: If the current A1[i]and A2[j]the same (ie, there is a new public element) so dp[i][j]=max(dp[i][j],dp[i−1][j−1]+1);if not identical, that can not be updated common elements, consider inheritance: dp[i][j]=max(dp[i−1][j],dp[i][j−1]. But for this problem is simple algorithm \ (n ^ 2 \) because \ (10 ^ 5 \) size of the data exceeds the time range, the algorithm sets nlogn considered as two full permutation sequences are 1 to n, then the two mutually different sequences and the same elements, that is to say is just a different position, then we will array by a number of positions in the sequence a B sequence is represented - is the longest common subsequence because bit backward alignment , so that a position in the sequence of each element if the incremented sequence b, b will be described in the overall number of positions after this number in a partial, may be considered for inclusion LCS-- then converted for recording ask nlogn map array of new locations in the LIS.
n = 0;
   cin >> n;
   for (i = 0; i < n; ++i)
       cin >> a1[i];
   for (i = 0; i < n; ++i)
       cin >> a2[i];
   for (i = 0; i < n; ++i)
       dp[a1[i]] = i; //将a1数组中的数的顺序载入
       int len = 0;
   for (i = 1; i <n; ++i)
   {
       if (dp[a2[len]] < dp[a2[i]])
           a2[++len] = a2[i];
       else //二分查找
       {
           int j = 0, k = len,mid;
           while(j<k)
           {
               mid = (j + k) / 2;
               if (dp[a2[mid]] > dp[a2[i]])
                   k = mid;
               else j = mid + 1;
           }
           if(dp[a2[j]]>dp[a2[i]])
               a2[j] = a2[i];
       }
   }
   cout << len+1<< endl;
  • Luo Gu P1020 missile intercept
    this question is to ask a child does not rise monotonically longest sequence (up to the number of interceptor missiles) and one of the longest monotonically increasing subsequence (how many interceptor system requires a minimum).
int a[MAX],a2[MAX],dp[2][MAX];

    n = 0;
    while ((cin >> a[n]))
        ++n;
    dp[0][0] = a[0];
    dp[1][0] = a[0];
    int len1 = 0, len2 = 0;
    for (i = 1; i <n; ++i)
    {
        if (dp[0][len1] >= a[i])
            dp[0][++len1] = a[i];
        else
        {
            int p= upper_bound(dp[0], dp[0]+ len1, a[i], greater<int>()) - dp[0];
            dp[0][p] = a[i];
        }//最长单调不升序列
        if (dp[1][len2] < a[i])
            dp[1][++len2] = a[i];
        else
        {
            int p= lower_bound(dp[1], dp[1]+ len2, a[i]) - dp[1];
            dp[1][p] = a[i];
        }//最长单调递减序列
    }
    cout << len1 + 1 << '\n' << len2 + 1 << endl;
  • Valley City Los P2782 friendship
    the north bank of the South Bank or sorted into one side has a monotonically increasing, demand on the other side by the single longest sequence to
  struct node
{
    int n, s;
}a[2*MAX+10];
bool cmp(struct node x, struct node y)
{
    return x.n < y.n;
}
int main()
{
    cin >> n;
    for (i = 1; i <= n; ++i)
        scanf("%d%d", &a[i].n,&a[i].s);
    int len= 1;
    sort(a + 1, a + n + 1,cmp);//排序
    for (i = 2; i <= n; ++i)//二分求最长单增子序列
    {
        if (a[i].s > a[len].s)
        {
            a[++len].s = a[i].s;
        }
        else
        {
            j = 1, k = len;
            int mid;
            while(j<k)
            {
                mid = (j + k) / 2;
                if (a[mid].s > a[i].s)
                    k = mid;
                else j = mid + 1;
            }
            a[j].s = min(a[j].s, a[i].s);
        }
    }
    printf("%d", len);
    return 0;
}
  • OpenJ_Bailian - 2995
#include <iostream>
#include <cstdio>
using namespace std;
int n,i,j;
int a[1200],f1[1200],f2[1200],maxn;
int main()
{
    scanf("%d",&n);
    for (i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for (i=1;i<=n;i++)
        f1[i]=f2[i]=1;
    for (i=1;i<=n;i++)
        for (j=1;j<i;j++)
            if (a[i]>a[j])    
            f1[i]=max(f1[i],f1[j]+1);//求首元素开始到某一元素结束的最长递增序列长度
    for (i=n;i>=1;i--)
        for (j=n;j>i;j--)
            if (a[i]>a[j])    
            f2[i]=max(f2[i],f2[j]+1);//求某一元素开始到尾元素的最长递减序列长度
    for (i=1;i<=n;i++)
        maxn=max(maxn,f1[i]+f2[i]-1); //遍历求除最大值
    printf("%d\n",maxn);
    return 0;
}
  • Print dp path

  • Luo dig mines P2196 valley
    defined state f[i]maximum value at the end of the i-th node, then f[i]=max{f[j]}+a[i] (g[j][i]=1),
void print(int x)//打印前驱节点
{
    if (pre[x] == 0)//起始节点无前驱节点
    {
        printf("%d", x);
        return;
    }
    else print(pre[x]);
    printf(" %d", x);
}
int main()
{
    cin >> n;
    for (i = 1; i <= n; ++i)
        cin >> a1[i];
    for(i=1;i<n;++i)
        for (j = i + 1; j <= n; ++j)
            cin >> a[i][j];
    for (i = 1; i <= n; ++i)
    {
        for (j = 1; j <= n; ++j)
            if (a[j][i] == 1&&dp1[j]>dp1[i])
            {
                dp1[i] =dp1[j];
                pre[i] = j;//i点由j点到达
            }
        dp1[i] += a1[i];
        if (dp1[i] > sum)
        {
            sum = dp1[i];
            k = i;//记录结束的节点
        }
    }
    print(k);
    cout << '\n'<< sum << endl;
}
  • Luo Gu P2066 machine distribution
    f (i, j) represents the j machine to give maximum profit 1 ~ i's, w[i][j]represents the i-company profit transfer equation j machine is f(i,j)=max(k∈[0,j]){f(i+1,j-k)+w[i][j]}focused on how to optimize the size of the array (although this title data minimal), it is noted only used the transfer equation in f (i + 1, l) (l∈ [0, j]), so long as the descending enumeration j i can be removed in this dimensional array dp
    Next is output, when k takes a maximum value by ans stored array f (i, j), then sequentially obtains f (i, j) is f (i + 1 ,?) can be transferred from the output of the program . To lexicographically smallest positive sequence enumeration k so Comparative f [j] and f [jk] + w [i ] [k] and the like can not take the size of; i descending so enumeration (if positive sequence pieces to facilitate positive sequence output for answer is i f (n, m), begin output from n, if you want to save the positive sequence output would have an array).
int main()
{
    cin >> n>>m;
    sum = 0;
    for (i = 1; i <= n; ++i)
        for (j = 1; j <= m; ++j)
            scanf("%d", &a[i][j]);
    dp1[0] = 0;
    for (i = n; i > 0; --i)
        for (j = m; j >=0; --j)
            for (k = 1; k <= j; ++k)
                if (dp1[j-k] + a[i][k] > dp1[j])
                {
                    dp1[j] = dp1[j-k] + a[i][k];
                    a1[i][j] = k;
                }
    cout << dp1[m];
    for (i = 1, j = m; i <= n; ++i)
    {
        cout << endl << i << " " << a1[i][j];
        j -= a1[i][j];  
    }
    return 0;
}
  • Knapsack problem

    Backpack nine stresses
  • 01 backpack
    most basic knapsack problem, characterized by: each item is only one, you can select or leave out.
    Subproblem defined by the state: i.e., F [i] [v] denotes the front i items placed exactly a maximum capacity value v backpack available. The state transition equation is: f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}"i before the items placed in the backpack capacity of v in" The sub-problem, if only consider the strategy of the i-items (or leave), then it can only be converted to a i-1 involves the problem before the items. If you hold the i items, then the problem is transformed into "i-1 before the capacity of items placed in the backpack v"
    time and space complexity of the above process are O (N * V), wherein the time complexity of basic can no longer optimized, but it may be optimized spatial complexity to O (V).
    How to consider the above state transition equation to achieve, there is definitely a main loop i = 1..N, each calculated two-dimensional array f[i][0..V]of all the values. If only use an array f[0..V], you need to ensure that after the end of the i-th cycle f[v]is defined in our state representation f[i][v], because f[i][v]by f[i-1][v]and f[i-1][v-c[i]]pushing two sub-problems from recurrence, to ensure that f[i][v]when (ie push the i-th main loop f[v]time) can be obtained f[i-1][v]and f[i-1][v-c[i]]a value. In fact, each main loop we push v = V..0 order f[v]to assure that each push f[v]when f[v-c[i]]saving the status of f[i-1][v-c[i]]values.
    The optimal solution of the knapsack problem we see in the title, in fact, there are two less the same question is asked. Some questions asked during the optimal solution "just filled backpack", and some topics are not required to be filled backpack. Realizing of a difference between the two is a different question is asked during initialization. If the first question is asked, requires just filled bag, then the initialization except F [0] is 0 the other f [1..V] are set to -∞, so as to ensure the resulting f [N] a backpack filled with exactly the optimal solution. If the backpack is not required to be filled, but only want the price to as large as possible, initialization should be f [0..V] are all set to 0. The former maximum value of all the other volume must be obtained from 0 to obtain a filled bag, the latter may be of any volume as starting empty backpack, the remainder can be any volume. This trick can be extended to other types of knapsack problem
    Luo Gu herbs P1048
scanf("%d%d", &m, &n);
        for (i = 1; i <= n; ++i)
            scanf("%d%d", a1 + i, a2+i);

        for (i = 0; i <= m; ++i)
            dp[i] = 0;
        for (i = 1; i <= n; ++i)
            for (j =m ; j >= 0; --j)
                if (a1[i]<=j)
                    dp[j] = max(dp[j], dp[j-a1[i]] + a2[i]);
    
        printf("%d\n",dp[m]);

Luo Gu P2871 [USACO07DEC] Bracelet Charm Bracelet

        scanf("%d%d", &n, &m);
        for (i = 1; i <= n; ++i)
            scanf("%d%d", a[0] + i, a[1] + i);
        for (i = 0; i <= n; ++i)
            for (j = m; j >= a[0][i]; --j)
                dp[j] = max(dp[j - a[0][i]] + a[1][i], dp[j]);

        printf("%d\n",dp[m]);

Digital combination OpenJ_Bailian - 4004

    dp1[0] = 1;\\初始化和为0的方式为1
    for (i = 0; i < n; ++i)
        for (j = m; j >= 1; --j)
            if (a1[i] <= j && dp1[j - a1[i]] != 0)
                dp1[j] += dp1[j - a1[i]];\\得到和为j的组合方式
                printf("%d\n", dp1[m]);
  • Full backpack
    full 01 knapsack problem knapsack is very similar, except that each item can take unlimited pieces. So that F [i] [v] denotes the i article just before into a maximum weight capacity of the backpack v. State transition equation can be written in a different strategy for each item, like this: f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}.
    The most optimal way is to change the structure of the second field cycle from zero cycle, such f[i][v]may be formed f[i][v-c[i]]+w[i]to give
    crazy herbs Los Valley P1616
for (i = 1; i <= n; ++i)
            scanf("%d%d", a[0] + i, a[1] + i);
        for (i = 1; i <= n; ++i)
            for (j = a[0][i]; j<= m; ++j)\\从a[0][i]开始
                dp[j] = max(dp[j], dp[j- a[0][i]] + a[1][i]);
  • Other use

  • The largest sub-matrix HDU - 1559
int dp[1100][1100];
int MAX;
int main()
{

    int i,j,n,m,T,x,y;
    scanf("%d",&T);
    while(T--)
    {

       scanf("%d %d %d %d",&n,&m,&x,&y);

       MAX=0;

       memset(dp,0,sizeof(dp));

       for(i=1;i<=n;i++)
         for(j=1;j<=m;j++)
         {

              scanf("%d",&dp[i][j]);

              dp[i][j]+=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1];//状态转移方程 
              if(i>=x&&j>=y) //矩阵大小为x*y
              {
                 MAX=max(MAX,dp[i][j]-dp[i][j-y]-dp[i-x][j]+dp[i-x][j-y]);
              }           

         }

       printf("%d\n",MAX);

    }

    return 0;
}
  • Abstract peanut OpenJ_Bailian - 3727
cin >> t;
    while (t--)
    {
        cin >> m >> n;
        sum = 0;
        memset(a, 0, sizeof(a));
        for (j = 1; j <= m; ++j)
            for (i = 1; i <= n; ++i)
            {
                cin >> a[j][i];
                a[j][i] += max(a[j - 1][i], a[j][i - 1]);//只能由左方或上方走到
            }
        cout << a[m][n] << endl;
    }

Guess you like

Origin www.cnblogs.com/luanma-2333/p/11257110.html