动态规划的相关优化

题目链接vjudge.net/contest/245961

(以后不做特殊说明密码都为hpuacm)

                                              A - 矩阵取数问题

 

一个N*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,从左上走到右下,只能向下向右走,求能够获得的最大价值。

例如:3 * 3的方格。

 

1 3 3

2 1 3

2 2 1

 

能够获得的最大价值为:11。

Input

第1行:N,N为矩阵的大小。(2 <= N <= 500) 
第2 - N + 1行:每行N个数,中间用空格隔开,对应格子中奖励的价值。(1 <= Nii <= 10000)

Output

输出能够获得的最大价值。

Sample Input

3
1 3 3
2 1 3
2 2 1

Sample Output

11

先来一道入门题,嘿嘿简单。

#include<bits/stdc++.h>
using namespace std;

int arr[505][505];
long long dp[505][505];
int main()
{
    int n;
    scanf("%d", &n);
    memset(dp, 0, sizeof(dp));
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        {
            scanf("%d", &arr[i][j]);
        }
    dp[1][1] = arr[1][1];
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        {
            dp[i][j] = max(dp[i-1][j],dp[i][j-1]) + arr[i][j];
        }
    printf("%lld\n", dp[n][n]);
    return 0;
}

           

                                       B - 矩阵取数问题 V2

 

一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。

 

例如:3 * 3的方格。

 

1 3 3

2 1 3

2 2 1

 

能够获得的最大价值为:17。1 -> 3 -> 3 -> 3 -> 1 -> 2 -> 2 -> 2 -> 1。其中起点和终点的奖励只计算1次。

Input

第1行:2个数M N,中间用空格分隔,为矩阵的大小。(2 <= M, N <= 200) 
第2 - N + 1行:每行M个数,中间用空格隔开,对应格子中奖励的价值。(1 <= Ai,ji,j <= 10000)

Output

输出能够获得的最大价值。

Sample Input

3 3
1 3 3
2 1 3
2 2 1

Sample Output

17

 

难度升级,按照51nod的排名,这是一道5级难度的题,而A题是1级难度的。。。不过我们不慌,在难能难到哪去,我们还有度娘嘛。

这道题因为要走两边找到和的最大值,如果用两次dp求解是不对的。这样想,从左上走到右下,再从右下到左上,是不是和安排两个点同时从左上到右下一样?我们就可以建立一个3维dp,第一位表示步数,第二位表示x1,第三位表示x2(x1,x2为横坐标),

因为点只能往右下走,所以步数与横坐标的差值就是纵坐标(牛啤!)这样我们就可以建立动态转移方程了。

(注意3维dp容易超限,注意大小,另外,dp第一位应是后两位之和)

 

#include<bits/stdc++.h>
using namespace std;

int arr[202][202];
int dp[402][202][202];

int main()
{
    int n, m;
    cin>>m>>n;
    memset(dp, 0, sizeof(dp));
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            cin>>arr[i][j];
        }
    for(int i = 2; i <= n + m; i++)
        for(int j = 1; j <= n && i-j >= 1; j++)
            for(int k = 1; k <= n && i-k >= 1; k++)
            {
                dp[i][j][k] = max(max(dp[i - 1][j - 1][k - 1], dp[i - 1][j - 1][k]),
                                   max(dp[i - 1][j][k - 1], dp[i - 1][j][k]))
                              + arr[j][i - j];
                if(j != k)
                    dp[i][j][k] += arr[k][i - k];
            }
    printf("%d\n", dp[n + m][n][n]);
    return 0;
}

 

 

                                                  D - 子串查询

 

度度熊的字符串课堂开始了!要以像度度熊一样的天才为目标,努力奋斗哦! 

为了检验你是否具备不听课的资质,度度熊准备了一个只包含大写英文字母的字符串 A[1,n]=a1a2⋯anA[1,n]=a1a2⋯an,接下来他会向你提出 qq 个问题 (l,r)(l,r),你需要回答字符串 A[l,r]=alal+1⋯arA[l,r]=alal+1⋯ar 内有多少个非空子串是 A[l,r]A[l,r] 的所有非空子串中字典序最小的。这里的非空子串是字符串中由至少一个位置连续的字符组成的子序列,两个子串是不同的当且仅当这两个子串内容不完全相同或者出现在不同的位置。 

记 |S||S| 为字符串 SS 的长度,对于两个字符串 SS 和 TT ,定义 SS 的字典序比 TT 小,当且仅当存在非负整数 k(≤min(|S|,|T|))k(≤min(|S|,|T|)) 使得 SS 的前 kk 个字符与 TT 的前 kk 个字符对应相同,并且要么满足 |S|=k|S|=k 且 |T|>k|T|>k,要么满足 k<min(|S|,|T|)k<min(|S|,|T|) 且 SS 的第 k+1k+1 个字符比 TT 的第 k+1k+1 个字符小。例如 "AA" 的字典序比 "AAA" 小,"AB" 的字典序比 "BA" 小。

Input

第一行包含一个整数 TT,表示有 TT 组测试数据。 

接下来依次描述 TT 组测试数据。对于每组测试数据: 

第一行包含两个整数 nn 和 qq,表示字符串的长度以及询问的次数。 

第二行包含一个长为 nn 的只包含大写英文字母的字符串 A[1,n]A[1,n]。 

接下来 qq 行,每行包含两个整数 li,rili,ri,表示第 ii 次询问的参数。 

保证 1≤T≤101≤T≤10,1≤n,q≤1051≤n,q≤105,1≤li≤ri≤n1≤li≤ri≤n。

Output

对于每组测试数据,先输出一行信息 "Case #x:"(不含引号),其中 x 表示这是第 xx 组测试数据,接下来 qq 行,每行包含一个整数,表示字符串 A[l,r]A[l,r] 中字典序最小的子串个数,行末不要有多余空格。

Sample Input

1
2 3
AB
1 1
1 2
2 2

Sample Output

Case #1:
1
1
1

 

这道题真的太作了,题目很难理解,其实就是让你在他给点范围内找出字典序最小的字母的个数,也就是说,有A,就数A有几个,没有就找B,以此类推。输入的时候从第1位起,不是第0位(dp不到)。

(提示,注释的地方很重要)

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;
char s[maxn];
int sum[30][maxn];

int main()
{
    int t, ans;
    scanf("%d", &t);
    for(int c = 1; c <= t; c++)
    {
        int n, q;
        scanf("%d%d", &n, &q);
        scanf("%s", s+1);
        ans = 0;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j < 26; j++)
            {
                if(j + 'A' == s[i]) sum[j][i] = sum[j][i-1] + 1;
                else sum[j][i] = sum[j][i-1];
            }
        }
        int l, r;
        printf("Case #%d:\n", c);
        for(int i = 0; i < q; i++)
        {
            scanf("%d%d", &l, &r);
            for(int j = 0; j < 26; j++)
            {
                if(sum[j][r] - sum[j][l-1]!=0)//l-1是因为区间(l,r)包括l;如果是 
                                              //sum[j][l]则l边界会被减去                                                                                                                                                     
                {
                    ans = sum[j][r]-sum[j][l-1];
                    break;
                }
            }
            cout<<ans<<endl;
        }

    }
    return 0;
}

 

                                                Polycarp and Div 3

 

Polycarp likes numbers that are divisible by 3.

He has a huge number ss. Polycarp wants to cut from it the maximum number of numbers that are divisible by 33. To do this, he makes an arbitrary number of vertical cuts between pairs of adjacent digits. As a result, after mm such cuts, there will be m+1m+1 parts in total. Polycarp analyzes each of the obtained numbers and finds the number of those that are divisible by 33.

For example, if the original number is s=3121s=3121, then Polycarp can cut it into three parts with two cuts: 3|1|213|1|21. As a result, he will get two numbers that are divisible by 33.

Polycarp can make an arbitrary number of vertical cuts, where each cut is made between a pair of adjacent digits. The resulting numbers cannot contain extra leading zeroes (that is, the number can begin with 0 if and only if this number is exactly one character '0'). For example, 007, 01 and 00099 are not valid numbers, but 90, 0 and 10001 are valid.

What is the maximum number of numbers divisible by 33 that Polycarp can obtain?

Input

The first line of the input contains a positive integer ss. The number of digits of the number ss is between 11 and 2⋅1052⋅105, inclusive. The first (leftmost) digit is not equal to 0.

Output

Print the maximum number of numbers divisible by 33 that Polycarp can get by making vertical cuts in the given number ss.

Examples

Input

3121

Output

2

Input

6

Output

1

Input

1000000000000000000000000000000000

Output

33

Input

201920181

Output

4

Note

In the first example, an example set of optimal cuts on the number is 3|1|21.

In the second example, you do not need to make any cuts. The specified number 6forms one number that is divisible by 33.

In the third example, cuts must be made between each pair of digits. As a result, Polycarp gets one digit 1 and 3333 digits 0. Each of the 3333 digits 0 forms a number that is divisible by 33.

In the fourth example, an example set of optimal cuts is 2|0|1|9|201|81. The numbers 00, 99, 201201 and 8181 are divisible by 33.

 

 

这道题wa了5次 /笑哭,一道dp题,硬是做成了if判断题。主要就是找规律啦,神奇的是,最多不超过三个数都能被三整除

(原理:自己悟悟,嘿嘿)

#include <bits/stdc++.h>
using namespace std;

const int maxn = 2e5 + 10;

int main()
{
    char s[maxn];
    int ans = 0;
    cin>>s;
    if(strlen(s) == 1&&s[0] == '0') cout<<1<<endl;
    else
    {
        for(int i = 0; i < strlen(s); i++)
        {
            if((s[i] - '0')%3 == 0)
            {
                ans++;
                continue;
            }
            if(i+1<strlen(s)&&((s[i] - '0')%3 + s[i+1]-'0')%3 == 0)
            {
                ans++;
                i++;
                continue;
            }
            if(i+1<strlen(s)&&(s[i+1]-'0')%3 == 0)
            {
                ans++;
                i++;
                continue;
            }
            if(i+2<strlen(s))
            {
                ans++;
                i+=2;
                continue;
            }
        }
        cout<<ans<<endl;
    }

    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值