20200426DP训练总结

1.Multiplication Puzzle
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
10150 + 50205 + 10505 = 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
15020 + 1205 + 1015 = 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问题。由于不确定有几组输入,所以用while来完成输入过程。dp[i][j]表示第i个数到第j个数之间的数的最小值,先初始化数组,再确定状态转移方程。其中两两相邻的数中间没有数可删,即该区间内花费初始化为0。这样从最小区间长度2开始,统计3个数存在时的最小花费。通过选择一个数k介于i和j之间,继而平移这段区间,求出所有该长度的花费,求得小区间后即可求更大的区间,这样不断递推下去。最终得到区间长度为n的整个序列的最小花费。
由于我不大会写用scanf写的while,所以出现了几次out put limited,主要原因是

while(~scanf("%d",&n))

没有写~符号
下面附上AC代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=105;
long long dp[N][N],a[N],n,i,j,k,ans;
int main()
{
    while(~scanf("%d",&n))
    {
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
          for(ans=2;ans<=n;ans++)
            for(i=1;i<=n-ans;i++)
            {
                j=i+ans;
                dp[i][j]=1e12;
                for(k=i;k<j;k++)
                    dp[i][j]=min(dp[i][j],a[i]*a[k]*a[j]+dp[i][k]+dp[k][j]);
            }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}

2.String painter

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
这个题的大意是求从第一个字符串变成第二个字符串最少的步骤,每次操作时将a中任意一段变成任意一个字母所组成的段,如ddddd abcba 需要三步:1、aaaaa 2、abbba 3、abcba
这个题如果只看第二个串的话,可以有以下思路:dp[i][j]表示i到j的操作的最小值,然后对于i,枚举k的位置,如果i和k的位置上字符相同,dp[i][j] = dp[i+1][k] + dp[k+1][j](区间dp的惯用套路),然后更新最小值就可以。再一并考虑两个串,从a串变到b串,我用了一个数组s[i]来表示0~i的一个最小值,然后直接枚举所有情况取最小值即可。
AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 110
using namespace std;
int dp[N][N],s[N],i,j,k,ans;
char a[N],b[N];
int main(){
	while(scanf("%s%s",a+1,b+1)==2){
		memset(dp,0,sizeof(dp));
		int len=strlen(a+1);
		for(i=1;i<=len;i++)
            dp[i][i]=1;
		for(ans=2;ans<=len;ans++)
            for(i=1;i<=len-ans+1;i++)
            {
		 	    j=i+ans-1;
		 	    if(b[i]==b[j])
		 	        dp[i][j]=dp[i][j-1];
                else dp[i][j]=dp[i][j-1]+1;
		 	    for(k=i;k<j;k++)    
		 		    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
		    }
		 for(i=1;i<=len;i++)
		     s[i]=dp[1][i];
		 for(i=1;i<=len;i++)
		 {
		 	if(a[i]==b[i])
		 	    s[i]=s[i-1];
		 	else
		 	{
		 		for(int k=1;k<i;k++)
		 		    s[i]=min(s[i],s[k]+dp[k+1][i]);
            }
		 }
		 printf("%d\n",s[len]);
	}
	return 0;
}

3.Food Delivery

When we are focusing on solving problems, we usually prefer to stay in front of computers rather than go out for lunch. At this time, we may call for food delivery.Suppose there are N people living in a straight street that is just lies on an X-coordinate axis. The ithperson’s coordinate is Xi meters. And in the street there is a take-out restaurant which has coordinates X meters. One day at lunchtime, each person takes an order from the restaurant at the same time. As a worker in the restaurant, you need to start from the restaurant, send food to the N people, and then come back to the restaurant. Your speed is V-1 meters per minute.You know that the N people have different personal characters; therefore they have different feeling on the time their food arrives. Their feelings are measured by Displeasure Index. At the beginning, the Displeasure Index for each person is 0. When waiting for the food, the ith person will gain Bi Displeasure Index per minute.If one’s Displeasure Index goes too high, he will not buy your food any more. So you need to keep the sum of all people’s Displeasure Index as low as possible in order to maximize your income. Your task is to find the minimal sum of Displeasure Index.InputThe input contains multiple test cases, separated with a blank line. Each case is started with three integers N ( 1 <= N <= 1000 ), V ( V > 0), X ( X >= 0 ), then N lines followed. Each line contains two integers Xi ( Xi >= 0 ), Bi ( Bi >= 0), which are described above.You can safely assume that all numbers in the input and output will be less than 231 - 1.Please process to the end-of-file.OutputFor each test case please output a single number, which is the minimal sum of Displeasure Index. One test case per line.Sample Input5 1 0
1 1
2 2
3 3
4 4
5 5

Sample Output
55
这个题想了好久,一直都是样例就卡住了,一直被困在了区间dp的传统思路二维数组里出不来,所以一直没想出正确的状态转移方程,没忍住,去搜了题解,看了看大佬的思路,觉得自己还是思路太局限,做题太少没有经验,大佬就是大佬。
这个题大意时在一条直线上,有一些需要送饭的客户,每一个客户有一个不满意的值,随着时间的增加而增加,题目是让我们求使得所有客户不满意度最小值。
此题用了三维数组,dp[i][j][[0]表示送完[i,j]区间停在左边,dp[i][j][1]表示送完[i,j]区间停在右边,其实还是区间dp经典ijk问题,只不过用到三维数组我就想不过来了。下面贴一下AC代码

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define INF 99999999999
using namespace std;
int dp[1101][1101][2];
int sum[1101];
struct node
{
    int x,v;
}s[11001];
bool cmp(node a,node b)
{
      return a.x<b.x;
}
int X,n,speed;
int i,j,k,ans;
int get_sum(int le,int ri)
  {
      if(le>ri)return 0;
      return sum[ri]-sum[le-1];
  }
int main()
{
    while(scanf("%d%d%d",&n,&speed,&X)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&s[i].x,&s[i].v);

        }
        s[++n].x=X;
        s[n].v=0;
        sort(s+1,s+n+1,cmp);
        memset(sum,0,sizeof(sum));
        for(i=1;i<=n;i++)
            sum[i]=sum[i-1]+s[i].v;
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                dp[i][j][0]=dp[i][j][1]=INF;
        ans=0;
        for(i=1;i<=n;i++)
            if(s[i].x==X)
            {
                ans=i;
                break;
            }
        dp[ans][ans][0]=0;
        dp[ans][ans][1]=0;
        for(i=ans;i>=1;i--)
            for(j=ans;j<=n;j++)
            {
                if(i==j)continue;
                int umt=get_sum(1,i-1)+get_sum(j+1,n);
                dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][0]+(umt+s[j].v)*(s[j].x-s[i].x));
                dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][1]+(umt+s[j].v)*(s[j].x-s[j-1].x));
                dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][0]+(umt+s[i].v)*(s[i+1].x-s[i].x));
                dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][1]+(umt+s[i].v)*(s[j].x-s[i].x));
            }
        int Max=min(dp[1][n][0],dp[1][n][1]);
        printf("%d\n",Max*speed);
    }
    return 0;
}

4.Proud Merchants
Recently, iSea went to an ancient country. For such a long time, it was the most wealthy and powerful kingdom in the world. As a result, the people in this country are still very proud even if their nation hasn’t been so wealthy any more.
The merchants were the most typical, each of them only sold exactly one item, the price was Pi, but they would refuse to make a trade with you if your money were less than Qi, and iSea evaluated every item a value Vi.
If he had M units of money, what’s the maximum value iSea could get?

Input
There are several test cases in the input.

Each test case begin with two integers N, M (1 ≤ N ≤ 500, 1 ≤ M ≤ 5000), indicating the items’ number and the initial money.
Then N lines follow, each line contains three numbers Pi, Qi and Vi (1 ≤ Pi ≤ Qi ≤ 100, 1 ≤ Vi ≤ 1000), their meaning is in the description.

The input terminates by end of file marker.

Output
For each test case, output one integer, indicating maximum value iSea could get.

Sample Input
2 10
10 15 10
5 10 5
3 10
5 10 5
3 5 6
2 7 3
Sample Output
5
11
这个题的大意就是首先给出物品数量和手中资金
再给出每样物品价格,需要购买时手中至少需要多少资金,还有物品本身的价值,要求求出最大资金。
这个题我刚开始是按价值排序的,一直觉得没有错,所以提交了好几遍WA,后来仔细想了想,应该是按照q-p排序,这样才能保证差额最小,才能得到最优解。关于dp部分,仅用双重循环即可,第二层循环意味剩余的钱大于q才能购买。
下面上代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
    int p,q,v;
}a[505];
 bool cmp(node x,node y)
{
    return  x.q-x.p<y.q-y.p
}
 int main()
{
    int n,m,i,j;
    int dp[5555];
    while(~scanf("%d%d",&n,&m))
    {
        for(i = 0; i<n; i++)
            scanf("%d%d%d",&a[i].p,&a[i].q,&a[i].v);
        memset(dp,0,sizeof(dp));
        sort(a,a+n,cmp);
        for(i=0;i<n;i++)
        {
            for(j=m;j>=a[i].q;j--)
            {
                dp[j] = max(dp[j],dp[j-a[i].p]+a[i].v);
            }
        }
        printf("%d\n",dp[m]);
    }
    return 0;
}

5.Cheapest Palindrome
Keeping track of all the cows can be a tricky task so Farmer John has installed a system to automate it. He has installed on each cow an electronic ID tag that the system will read as the cows pass by a scanner. Each ID tag’s contents are currently a single string with length M (1 ≤ M ≤ 2,000) characters drawn from an alphabet of N (1 ≤ N ≤ 26) different symbols (namely, the lower-case roman alphabet).

Cows, being the mischievous creatures they are, sometimes try to spoof the system by walking backwards. While a cow whose ID is “abcba” would read the same no matter which direction the she walks, a cow with the ID “abcb” can potentially register as two different IDs (“abcb” and “bcba”).

FJ would like to change the cows’s ID tags so they read the same no matter which direction the cow walks by. For example, “abcb” can be changed by adding “a” at the end to form “abcba” so that the ID is palindromic (reads the same forwards and backwards). Some other ways to change the ID to be palindromic are include adding the three letters “bcb” to the begining to yield the ID “bcbabcb” or removing the letter “a” to yield the ID “bcb”. One can add or remove characters at any location in the string yielding a string longer or shorter than the original string.

Unfortunately as the ID tags are electronic, each character insertion or deletion has a cost (0 ≤ cost ≤ 10,000) which varies depending on exactly which character value to be added or deleted. Given the content of a cow’s ID tag and the cost of inserting or deleting each of the alphabet’s characters, find the minimum cost to change the ID tag so it satisfies FJ’s requirements. An empty ID tag is considered to satisfy the requirements of reading the same forward and backward. Only letters with associated costs can be added to a string.

Input
Line 1: Two space-separated integers: N and M
Line 2: This line contains exactly M characters which constitute the initial ID string
Lines 3… N+2: Each line contains three space-separated entities: a character of the input alphabet and two integers which are respectively the cost of adding and deleting that character.
Output
Line 1: A single line with a single integer that is the minimum cost to change the given name tag.
Sample Input
3 4
abcb
a 1000 1100
b 350 700
c 200 800
Sample Output
900
Hint
If we insert an “a” on the end to get “abcba”, the cost would be 1000. If we delete the “a” on the beginning to get “bcb”, the cost would be 1100. If we insert “bcb” at the begining of the string, the cost would be 350 + 200 + 350 = 900, which is the minimum.
求将输入的字符串变为回文字符串的最小花费。
本题大体思路:d[i][j]表示把字符串str中区间i ~j (str[i~j])字符串改变为回文串的最小花费。
分三种情况,如果s[i][j]是回文字符串,dp[i][j] = dp[i+1][j-1]
如果s[i+1][j]是回文字符串,dp[i][j] = dp[i+1][j]+cost[i]
如果s[i][j-1]是回文字符串,dp[i][j] = dp[i][j-1]+cost[j]
以样例为例:
第一次k循环中先求所有区间长度为1的dp值,str[0~1]=“ab”,所以dp[0][1]就取min(dp[1][1]+cost[str[0]],dp[0][0]+cost[str[1]]),dp[1][1]+cost[str[0]]代表把"b"改变为回文串然后去掉左边的一个a或者在右边添加一个a(所以字母花费取其添加和删除的最小值即可),dp[0][0]+cost[str[1]]代表把"a"改变为回文串然后去掉右边的一个b或者在左边添加一个b,其余同理。 第二次k循环中求所有区间长度为2的dp值,str[0~2]=“abc”,所以dp[0][2]就取min(dp[1][2]+cost[str[0]],dp[0][1]+cost[str[2]]),dp[1][2]+cost[str[0]]代表把"bc"改变为回文串(dp[1][2])然后去掉左边的一个a或者在右边添加一个a,dp[0][1]+cost[str[2]]代表把"ab"改变为回文串(dp[0][1])然后去掉右边的一个c或者在左边添加一个c,其余同理。第三次k循环中求所有区间长度为3的dp值。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2e3 + 5;
const int INF = 0x3f3f3f3f;

char s[N];
int cost[30], dp[N][N];

int main(void)
{
	ios::sync_with_stdio(false);
	int n, m;
	cin >> n >> m; 
	cin >> s + 1;
	char c;
	int a, b;
	for (int i = 0; i < n; i++){
		cin >> c >> a >> b;
		cost[c - 'a'] = min(a, b);		
	}
	for (int len = 2; len <= m; len++){
		for (int i = 1; i <= m - len + 1; i++){
			int j = i + len - 1;
			if (s[i] == s[j])
				dp[i][j] = dp[i + 1][j - 1];
			else{
				dp[i][j] = INF;
				dp[i][j] = min(dp[i][j], dp[i + 1][j] + cost[s[i] - 'a']);
				dp[i][j] = min(dp[i][j], dp[i][j - 1] + cost[s[j] - 'a']);
			}
		}
	}
	cout << dp[1][m] << endl;
	return 0;
} 

再总结一下本周的cf吧,本周到目前为止打了两场cf,一场div3一场div2,div3那一场的C题因为不会使用scanf使得提交了好几次都是WA在第一个测试点上,比赛结束才知道把scanf改成cin就AC了,感觉很亏,div2那一场本来过了两个题,C题读了一个多小时也没看懂题目要求,很失落,第二天起来发现A题也被hack了,哭唧唧,还是太菜,继续努力吧,争取早日紫名(虽然很遥远)!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值