杭电ACM-LCY算法进阶培训班-专题训练(区间dp)

杭电ACM-LCY算法进阶培训班-专题训练(区间dp)

Two Rabbits

Problem Description
Long long ago, there lived two rabbits Tom and Jerry in the forest. On a sunny afternoon, they planned to play a game with some stones. There were n stones on the ground and they were arranged as a clockwise ring. That is to say, the first stone was adjacent to the second stone and the n-th stone, and the second stone is adjacent to the first stone and the third stone, and so on. The weight of the i-th stone is ai.

The rabbits jumped from one stone to another. Tom always jumped clockwise, and Jerry always jumped anticlockwise.

At the beginning, the rabbits both choose a stone and stand on it. Then at each turn, Tom should choose a stone which have not been stepped by itself and then jumped to it, and Jerry should do the same thing as Tom, but the jumping direction is anti-clockwise.

For some unknown reason, at any time , the weight of the two stones on which the two rabbits stood should be equal. Besides, any rabbit couldn’t jump over a stone which have been stepped by itself. In other words, if the Tom had stood on the second stone, it cannot jump from the first stone to the third stone or from the n-the stone to the 4-th stone.

Please note that during the whole process, it was OK for the two rabbits to stand on a same stone at the same time.

Now they want to find out the maximum turns they can play if they follow the optimal strategy.

Input
The input contains at most 20 test cases.
For each test cases, the first line contains a integer n denoting the number of stones.
The next line contains n integers separated by space, and the i-th integer ai denotes the weight of the i-th stone.(1 <= n <= 1000, 1 <= ai <= 1000)
The input ends with n = 0.

Output
For each test case, print a integer denoting the maximum turns.

Sample Input

1
1
4
1 1 2 1
6
2 1 1 2 1 3
0

Sample Output

1
4
5

Hint

For the second case, the path of the Tom is 1, 2, 3, 4, and the path of Jerry is 1, 4, 3, 2.
For the third case, the path of Tom is 1,2,3,4,5 and the path of Jerry is 4,3,2,1,5.

思路
两只兔子各选一块石头(重量相等),一只顺时针走,另一只逆时针走。最大的步数等于将原石头序列分为左右两部分,求两部分的最大回文子序列长度之和。
杭电ACM-LCY算法进阶培训班-专题训练(区间dp)
画个很粗糙的图解释一下为什么要将原序列分成左右两端,且答案为这两段的最大回文子序列长度之和:
例如,两只兔子最初都站在s[3]=1这块石头上(图中方块标出),第一只兔子(上方箭头)往右走,第二只(下方箭头)往左走。假设第一只兔子已经踩过的石头为1、2(上方横线标出),第二只也踩过1、2(下方横线标出)。在上面这两步中,兔子们踩的石头正好是序列2、1、1、2中的回文(尽管现在不是最大的回文子串)。
下一步,假设两只兔子都踩了3,这又是1、3这个序列的回文。
再下一步、两只兔子都踩2,这也是最后一步了,又回到了2、1、1、2这个序列中。
所以,在上面这个例子中,把2、1、1、2、1、3分为了2、1、1、2和1、3这左右两部分,兔子踩过的数都属于这两部分的回文子序列:”2、1、2“、”3“。
不过,由于起始位置的原因,兔子踩了这两个序列中的回文子序列,并没有踩最大回文子序列。不过,显然存在一种走法,使得兔子能够踩到最大回文子序列。
上面的例子只举例了两只兔子同起点的情况,当它们起点不在同一块石头上时,依然可以看作踩了左右两部分的回文子序列。
所以,问题就转化为,求一个序列中的所有子序列的最大回文子序列长度(有点绕,就是解决所有的子问题)。这里可以用动态规划来求出所有子问题的答案。
状态转移方程为:
d p [ i ] [ j ] = { d p [ i + 1 ] [ j − 1 ] + 2 , s [ i ] = = s [ j ] m a x ( d p [ i + 1 ] [ j ] , d p [ i ] [ j − 1 ] ) , d p [ i − 1 ] [ j − 1 ] = 1 dp[i][j]=\left\{ \begin{aligned} dp[i+1][j-1]+2 &,s[i]==s[j] \\ max(dp[i+1][j],dp[i][j-1]) &,dp[i-1][j-1]=1\\ \end{aligned} \right. dp[i][j]={dp[i+1][j−1]+2max(dp[i+1][j],dp[i][j−1])​,s[i]==s[j],dp[i−1][j−1]=1​
d p [ i ] [ j ] dp[i][j] dp[i][j]代表 [ i , j ] [i,j] [i,j]这个区间的最大回文子序列长度。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1005;
int dp[maxn][maxn],n,a[maxn];

int main(){
    while(scanf("%d",&n),n){
        for(int i=1;i<=n;i++)   scanf("%d",&a[i]),dp[i][i]=1;
        for(int len=2;len<=n;len++){
            for(int i=1;i+len-1<=n;i++){
                int j=i+len-1;
                if(a[i]==a[j])  dp[i][j]=dp[i+1][j-1]+2;
                else    dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
            }
        }
        int ans=0;
        for(int i=0;i<=n;i++)   ans=max(ans,dp[1][i]+dp[i+1][n]);
        printf("%d\n",ans);
    }
}

Dire Wolf

Problem Description
Dire wolves, also known as Dark wolves, are extraordinarily large and powerful wolves. Many, if not all, Dire Wolves appear to originate from Draenor.
Dire wolves look like normal wolves, but these creatures are of nearly twice the size. These powerful beasts, 8 - 9 feet long and weighing 600 - 800 pounds, are the most well-known orc mounts. As tall as a man, these great wolves have long tusked jaws that look like they could snap an iron bar. They have burning red eyes. Dire wolves are mottled gray or black in color. Dire wolves thrive in the northern regions of Kalimdor and in Mulgore.
Dire wolves are efficient pack hunters that kill anything they catch. They prefer to attack in packs, surrounding and flanking a foe when they can.
— Wowpedia, Your wiki guide to the World of Warcra

Matt, an adventurer from the Eastern Kingdoms, meets a pack of dire wolves. There are N wolves standing in a row (numbered with 1 to N from left to right). Matt has to defeat all of them to survive.

Once Matt defeats a dire wolf, he will take some damage which is equal to the wolf’s current attack. As gregarious beasts, each dire wolf i can increase its adjacent wolves’ attack by bi. Thus, each dire wolf i’s current attack consists of two parts, its basic attack ai and the extra attack provided by the current adjacent wolves. The increase of attack is temporary. Once a wolf is defeated, its adjacent wolves will no longer get extra attack from it. However, these two wolves (if exist) will become adjacent to each other now.

For example, suppose there are 3 dire wolves standing in a row, whose basic attacks ai are (3, 5, 7), respectively. The extra attacks bi they can provide are (8, 2, 0). Thus, the current attacks of them are (5, 13, 9). If Matt defeats the second wolf first, he will get 13 points of damage and the alive wolves’ current attacks become (3, 15).

As an alert and resourceful adventurer, Matt can decide the order of the dire wolves he defeats. Therefore, he wants to know the least damage he has to take to defeat all the wolves.

Input
The first line contains only one integer T , which indicates the number of test cases. For each test case, the first line contains only one integer N (2 ≤ N ≤ 200).

The second line contains N integers ai (0 ≤ ai ≤ 100000), denoting the basic attack of each dire wolf.

The third line contains N integers bi (0 ≤ bi ≤ 50000), denoting the extra attack each dire wolf can provide.

Output
For each test case, output a single line “Case #x: y”, where x is the case number (starting from 1), y is the least damage Matt needs to take.

Sample Input

2
3
3 5 7
8 2 0
10
1 3 5 7 9 2 4 6 8 10
9 4 1 2 1 2 1 4 5 1

Sample Output
Case #1: 17
Case #2: 74

Hint
In the first sample, Matt defeats the dire wolves from left to right. He takes 5 + 5 + 7 = 17 points of damage which is the least damage he has to take.

题意
有 n n n只狼排成一排,每只狼有一个直接伤害和一个辅助伤害,当你打败第 i i i只狼时,会受到第 i i i只狼的直接伤害和它左右两只狼的辅助伤害。打败第 i i i只狼后,它左右两只狼相邻。
思路
d p [ i ] [ j ] dp[i][j] dp[i][j]代表打败 [ i , j ] [i,j] [i,j]这个区间内所有的狼受到的伤害,特别注意:这里并没有把第 i − 1 i-1 i−1, j + 1 j+1 j+1只狼的辅助伤害计入
状态转移方程为:
d p [ i ] [ j ] = m i n i < = k < = j { d p [ i ] [ k − 1 ] + d p [ k + 1 ] [ j ] + a [ k ] + b [ i − 1 ] + b [ j + 1 ] } dp[i][j]=min_{i<=k<=j}\{dp[i][k-1]+dp[k+1][j]+a[k]+b[i-1]+b[j+1]\} dp[i][j]=mini<=k<=j​{dp[i][k−1]+dp[k+1][j]+a[k]+b[i−1]+b[j+1]}
状态转移方程的意义为:最后打败第k只狼,先打败区间 [ i , k − 1 ] [i,k-1] [i,k−1]、 [ k + 1 , j ] [k+1,j] [k+1,j]两个区间的狼

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=205;
int te,n,dp[maxn][maxn],a[maxn],b[maxn],ca;

int main(){
    scanf("%d",&te);
    while(te--){
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        memset(b,0,sizeof(b));
        for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)   scanf("%d",&b[i]);
        for(int len=1;len<=n;len++){
            for(int i=1;i+len-1<=n;i++){
                int j=i+len-1;
                dp[i][j]=1e9;
                for(int k=i;k<=j;k++)
                    dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+a[k]+b[i-1]+b[j+1]);
            }
        }
        printf("Case #%d: %d\n",++ca,dp[1][n]);
    }
}

String painter

Problem Description
There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations?

Input
Input contains multiple cases. Each case consists of two lines:
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.

Output
A single line contains one integer representing the answer.

Sample Input

zzzzzfzzzzz
abcdefedcba
abababababab
cdcdcdcdcdcd

Sample Output

6
7

思路
先计算一个空串涂为b所需的次数,再计算a涂为b的次数。
f [ i ] [ j ] f[i][j] f[i][j]代表把一个空串涂为b的[i,j]区间需要的最少次数,那么:
f [ i ] [ j ] = { f [ i ] [ j − 1 ] , b [ i ] = = s [ j ] m i n ( f [ i ] [ k ] + f [ k + 1 ] [ j ] ) , e l s e f[i][j]=\left\{ \begin{aligned} f[i][j-1] ,b[i]==s[j] \\ min(f[i][k]+f[k+1][j]) ,else\\ \end{aligned} \right. f[i][j]={f[i][j−1],b[i]==s[j]min(f[i][k]+f[k+1][j]),else​

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=110,inf=0x3f3f3f3f;
char a[maxn],b[maxn];
int f[maxn][maxn],dp[maxn],n;

int main(){
    while(~scanf("%s%s",a+1,b+1)){
        n=strlen(a+1);
        for(int i=1;i<=n;i++)   f[i][i]=1;
        for(int len=2;len<=n;len++)
            for(int i=1;i+len-1<=n;i++){
                int j=i+len-1;
                f[i][j]=inf;
                if(b[i]==b[j])  f[i][j]=f[i][j-1];
                else    for(int k=i;k<j;k++)
                    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
            }
        for(int i=1;i<=n;i++)   dp[i]=i==1?a[i]!=b[i]:inf;
        for(int i=2;i<=n;i++)
            if(a[i]==b[i])  dp[i]=dp[i-1];
            else    for(int j=0;j<i;j++)   dp[i]=min(dp[i],dp[j]+f[j+1][i]);
        printf("%d\n",dp[n]);
    }
}

You Are the One

Problem Description
  The TV shows such as You Are the One has been very popular. In order to meet the need of boys who are still single, TJUT hold the show itself. The show is hold in the Small hall, so it attract a lot of boys and girls. Now there are n boys enrolling in. At the beginning, the n boys stand in a row and go to the stage one by one. However, the director suddenly knows that very boy has a value of diaosi D, if the boy is k-th one go to the stage, the unhappiness of him will be (k-1)*D, because he has to wait for (k-1) people. Luckily, there is a dark room in the Small hall, so the director can put the boy into the dark room temporarily and let the boys behind his go to stage before him. For the dark room is very narrow, the boy who first get into dark room has to leave last. The director wants to change the order of boys by the dark room, so the summary of unhappiness will be least. Can you help him?

Input
  The first line contains a single integer T, the number of test cases. For each case, the first line is n (0 < n <= 100)
  The next n line are n integer D1-Dn means the value of diaosi of boys (0 <= Di <= 100)

Output
  For each test case, output the least summary of unhappiness .

Sample Input

2 
5
1 2 3 4 5
5
5 4 3 2 2

Sample Output

Case #1: 20
Case #2: 24

思路
d p [ i ] [ j ] = m i n ( d p [ i + 1 ] [ k ] + d p [ k + 1 ] [ j ] + ( k − i ) ∗ a [ i ] + ( k − i + 1 ) ∗ s u m k + 1 , j ) dp[i][j]=min(dp[i+1][k]+dp[k+1][j]+(k-i)*a[i]+(k-i+1)*sum_{k+1,j}) dp[i][j]=min(dp[i+1][k]+dp[k+1][j]+(k−i)∗a[i]+(k−i+1)∗sumk+1,j​)
d p [ i ] [ j ] dp[i][j] dp[i][j]代表第i个到第j个人第一个出场时的焦虑值。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
int te,n,ca,a[maxn],dp[maxn][maxn],sum[maxn];

int main(){
    scanf("%d",&te);
    while(te--){
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
        for(int i=n;i>0;i--)    sum[i]=sum[i+1]+a[i];
        for(int len=2;len<=n;len++)
            for(int i=1;i+len-1<=n;i++){
                int j=i+len-1;
                dp[i][j]=dp[i+1][j]+sum[i+1]-sum[j+1];
                for(int k=i+1;k<=j;k++)
                    dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]+(k-i)*a[i]+(k-i+1)*(sum[k+1]-sum[j+1]));
            }
        printf("Case #%d: %d\n",++ca,dp[1][n]);
    }
}
上一篇:ACM算法目录


下一篇:Spring框架知识点一