2018-08-03 区间DP和树形DP

  • A  -- 石子归并

  • Description

现在有n堆石子,第i堆有ai个石子。现在要把这些石子合并成一堆,每次只能合并相邻两个,每次合并的代价是两堆石子的总石子数。求合并所有石子的最小代价

  • Input

第一行包含一个整数T(T<=50),表示数据组数
每组数据第一行包含一个整数n(2<=n<=100),表示石子的堆数
第二行包含n个正整数ai(ai<=100),表示每堆石子的石子数

  • Output

每组数据仅一行,表示最小合并代价

  • Sample Input
2
4
1 2 3 4
5
3 5 2 1 4
  • Sample Output
19
33
  • 题目理解

这道题和之前的贪心合并果子很像,但是这里有一个限制条件,每次只能合并相邻的两堆果子,就不能保证贪心的思想能够实现,就算模拟实现也比较麻烦,这里想到区间DP是因为对于长度ij的果子最后一定是两堆果子合并而且为两段连续区间,就可以枚举断点,大区间由小区间推出来,dp(i,j)表示合并i-j区间的代价,因为合并两区间的代价值是总石子数,由于是连续区间,预处理一下以i结尾的总石子数,所以得出dp(i,j)=min(dp(i,k)+dp(k+1,j)+sum[j]-sum[i-1])

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
int dp[maxn][maxn],a[maxn];
int main(){
   int t,n;
   scanf("%d",&t);
   while(t--){
     scanf("%d",&n);
     memset(dp,0,sizeof(dp));
     memset(a,0,sizeof(a));
     for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        a[i]=a[i]+a[i-1];
     }
     for(int j=2;j<=n;++j){
        for(int i=j-1;i>=1;--i){
            int ans=1e9;
            for(int k=i;k<j;k++){
                ans=min(ans,dp[i][k]+dp[k+1][j]+a[j]-a[i-1]);
            }
            dp[i][j]=ans;
        }
     }
     printf("%d\n",dp[1][n]);
   }
   return 0;
}
  • B  -- Anniversary party

  • Description

There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The University has a hierarchical structure of employees. It means that the supervisor relation forms a tree rooted at the rector V. E. Tretyakov. In order to make the party funny for every one, the rector does not want both an employee and his or her immediate supervisor to be present. The personnel office has evaluated conviviality of each employee, so everyone has some number (rating) attached to him or her. Your task is to make a list of guests with the maximal possible sum of guests' conviviality ratings

  • Input

Employees are numbered from 1 to N. A first line of input contains a number N. 1 <= N <= 6 000. Each of the subsequent N lines contains the conviviality rating of the corresponding employee. Conviviality rating is an integer number in a range from -128 to 127. After that go T lines that describe a supervisor relation tree. Each line of the tree specification has the form: 
L K 
It means that the K-th employee is an immediate supervisor of the L-th employee. Input is ended with the line 
0 0

  • Output

Output should contain the maximal sum of guests' ratings

  • Sample Input
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
  • Sample Output
5
  • 题目理解

由于题目也没有明说根节点的表示,但对于一个结点一定会有存在或者不存在的情况,而与它相连的结点(父亲和儿子相同地位)都不能出现,由此我们可以直接建图,遍历的时候使用vis数组表示前面遍历的结点将图转变为有根树。转移过程对于i结点来说它可以存在或者不存在,使用dp(i,1)dp(i,0)来表示,如果它存在则其儿子都不存在;它不存在的时候儿子可以存在可以不存在取最大值,初始化dp(i,1)i阶层的数量,其他为0

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=6005;
vector<int>son[maxn];
int dp[maxn][2],vis[maxn];
int n;
void dfs(int root)
{
    vis[root]=1;
    for(int i=0;i<son[root].size();i++)
    {
        int v=son[root][i];
        if(!vis[v])
        {
            dfs(v);
            dp[root][1]+=dp[v][0];
            dp[root][0]+=max(dp[v][0],dp[v][1]);
        }
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<=n;i++)
            son[i].clear();
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&dp[i][1]);
            dp[i][0]=0;
        }
        int f,s;
        while(scanf("%d%d",&s,&f)!=EOF)
        {
            if(s==0&&f==0)
                break;
            son[f].push_back(s);
            son[s].push_back(f);
        }
        dfs(1);
        printf("%d\n",max(dp[1][0],dp[1][1]));
    }
    return 0;
}
  • C  -- Multiplication Puzzle

  • Description

The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken and the numbers on the cards on the left and on the right of it. It is not allowed to take out the first and the last card in the row. After the final move, only two cards are left in the row. 
The goal is to take cards in such order as to minimize the total number of scored points. 
For example, if cards in the row contain numbers 10 1 50 20 5, player might take a card with 1, then 20 and 50, scoring 

10*1*50 + 50*20*5 + 10*50*5 = 500+5000+2500 = 8000
If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be 

1*50*20 + 1*20*5 + 10*1*5 = 1000+100+50 = 1150

  • Input

The first line of the input contains the number of cards N (3 <= N <= 100). The second line contains N integers in the range from 1 to 100, separated by spaces

  • Output

Output must contain a single integer - the minimal score

  • Sample Input
6
10 1 50 50 20 5
  • Sample Output
3650
  • 题目理解

对于一段序列的相同操作求最小最大的情况使用区间DP就要适当的选取断点,我们知道最后一段区间一定会剩下首和尾,如果两段区间的首和尾为同一个数,那么这个中间值的消除就完成了长区间的消除所有元素,我们这时候思考如何由短区间表示,如上述分析我们得出dp(i,j)=dp(i,k)+dp(k,j)+a[i]*a[k]*a[j]

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
int dp[maxn][maxn],a[maxn];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
        for(int j=3;j<=n;++j){
            for(int i=j-1;i>=1;--i){
               for(int k=i+1;k<j;++k){
                 if(dp[i][j]==0) dp[i][j]=dp[i][k]+dp[k][j]+a[i]*a[k]*a[j];
                 else dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]);
                 //printf("%d %d %d\n",i,k,j);
                 //printf("!!%d %d %d\n",a[i],a[k],a[j]);
                 //printf("%d %d %d\n",dp[i][k],dp[k][j],dp[i][j]);
               }
            }
        }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}
  • D  -- Zuma

  • Description

Genos recently installed the game Zuma on his phone. In Zuma there exists a line of n gemstones, the i-th of which has color ci. The goal of the game is to destroy all the gemstones in the line as quickly as possible.

In one second, Genos is able to choose exactly one continuous substring of colored gemstones that is a palindrome and remove it from the line. After the substring is removed, the remaining gemstones shift to form a solid line again. What is the minimum number of seconds needed to destroy the entire line?

Let us remind, that the string (or substring) is called palindrome, if it reads same backwards or forward. In our case this means the color of the first gemstone is equal to the color of the last one, the color of the second gemstone is equal to the color of the next to last and so on

  • Input

The first line of input contains a single integer n (1 ≤ n ≤ 500) — the number of gemstones

The second line contains n space-separated integers, the i-th of which is ci (1 ≤ ci ≤ n) — the color of the i-th gemstone in a line

  • Output

Print a single integer — the minimum number of seconds needed to destroy the entire line

  • Sample Input
7
1 4 4 2 3 2 1
  • Sample Output
2
  • 题目理解

对于区间dp我们能够枚举断点来模拟所有情况,还要解决一个问题,如何在代码中表达回文问题。 如果一个区间(i,j)来说如果两端是相同的数字,那么它们的消除不会累计步骤,因为可以和小区间里任意一个一起消除,如果长度为2的区间就需要单独赋值为1。对大区间来说,如果其本身就是回文区间是最优,会由最小区间不断扩增扩增,但是区间dp值保持1不变,因为前面说的不累计步骤;如果需要切割成小区间,如果它能分成两个小的回文区间自然是次优,原理同上,如果都不行那么任意断点取所有可能最优值

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[505],dp[505][505];
int main()
{
    int n;
    scanf("%d",&n);
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        dp[i][i]=1;
    }
    for(int j=2;j<=n;++j){
        for(int i=j-1;i>=1;--i){
            if(a[i]==a[j]){
                //printf("!!%d  %d\n",i,j);
                if(i+1<=j-1){
                    if(dp[i][j]) dp[i][j]=min(dp[i][j],dp[i+1][j-1]);
                    else dp[i][j]=dp[i+1][j-1];
                }
                else dp[i][j]=1;
            }
            for(int k=i;k<j;++k){
                if(dp[i][j]) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
                else dp[i][j]=dp[i][k]+dp[k+1][j];
            }
            //printf("%d %d : %d\n",i,j,dp[i][j]);
        }
    }
    printf("%d\n",dp[1][n]);
    return 0;
}
  • I  -- Post Office

  • Description

There is a straight highway with villages alongside the highway. The highway is represented as an integer axis, and the position of each village is identified with a single integer coordinate. There are no two villages in the same position. The distance between two positions is the absolute value of the difference of their integer coordinates
Post offices will be built in some, but not necessarily all of the villages. A village and the post office in it have the same position. For building the post offices, their positions should be chosen so that the total sum of all distances between each village and its nearest post office is minimum
You are to write a program which, given the positions of the villages and the number of post offices, computes the least possible sum of all distances between each village and its nearest post office

  • Input

Your program is to read from standard input. The first line contains two integers: the first is the number of villages V, 1 <= V <= 300, and the second is the number of post offices P, 1 <= P <= 30, P <= V. The second line contains V integers in increasing order. These V integers are the positions of the villages. For each position X it holds that 1 <= X <= 10000

  • Output

The first line contains one integer S, which is the sum of all distances between each village and its nearest post office

  • Sample Input
10 5
1 2 3 6 7 9 11 22 44 50
  • Sample Output
9
  • 题目理解

i个村子建立j个邮局,如果i\leq j那么最近距离和最小值为0,因为可以在每个村子建一个及以上。对于i> j的情况,我们首先考虑如果是区间DP那么可以对i以及j进行断点枚举,拆分为子问题的时候其表现形式应该与前面保持一致。定义dp(i,j)为前i个村子建立j个邮局,那么当大区间拆分成两个小区间的时候我们可以用dp来表示,而对于后面的值无法表示。通过累积的思想如果我们在前面k个村落建立j-1个邮局(k< i),剩下后面的村落建立一个邮局,并且从中位数的原理连续几个村落建立一个邮局的最优值 容易得出,从而得出dp转移方程dp(i,j)=min(dp(k,j-1)+cot(k+1,i))其中(1\leq k< i)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=305;
const int maxm=35;
int cot[maxn][maxn],dp[maxn][maxm],a[maxn];
int main()
{
    int v,p;
    while(scanf("%d%d",&v,&p)!=EOF){
        for(int i=1;i<=v;++i)
            scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));//表示没有计算值
        memset(cot,0,sizeof(cot));
        for(int i=1;i<v;++i){
            for(int j=i+1;j<=v;++j){
                cot[i][j]=0;
                int mid=(i+j)/2;//当为整数的时候建在左边和建在右边的最小值相同
                for(int k=i;k<=j;++k)
                    cot[i][j]+=((a[k]-a[mid])>0?(a[k]-a[mid]):(a[mid]-a[k]));
            }
        }
        for(int j=2;j<=p;++j){
            for(int i=j+1;i<=v;++i){
                for(int k=1;k<i;++k){
                    if(j==2){
                      if(dp[i][j])
                        dp[i][j]=min(dp[i][j],cot[1][k]+cot[k+1][i]);
                      else
                        dp[i][j]=cot[1][k]+cot[k+1][i];
                      //printf("###%d %d\n",cot[1][k],cot[k+1][i]);
                    }
                    else{
                      if(dp[i][j])
                        dp[i][j]=min(dp[i][j],dp[k][j-1]+cot[k+1][i]);
                      else
                        dp[i][j]=dp[k][j-1]+cot[k+1][i];
                    }
                //printf("%d %d %d: %d\n",j,k,i,dp[i][j]);
                }
                //printf("%d\n",dp[i][j]);
            }
        }
        if(p==1)
            printf("%d\n",cot[1][v]);
        else
        printf("%d\n",dp[v][p]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zm_zsy/article/details/81434683