17AHU排位赛1 C题(经典DP)

problem

《snow halation》是μ’s的第二张单曲,其歌曲第二段伴奏结束后主唱穗乃果唱出“届けて”的同时,全场应援棒瞬间从白色转换成橙色。由于高度的整齐和效果的震撼,被称为“橙色的奇迹”,这也是“如果奇迹有颜色,那么一定是XX色”的最早来源。

这里写图片描述
现在,到了你来应援的时候了!
使用不同的应援形式有不同的效果(如里打、里跳、快挥、前挥、GT警报……),比如通常会GT警报后接着做里跳,这样能够让人更加沉迷到演唱会的气氛中。
经过精密的计算,我们终于得到了不同的应援形式连着对活跃气氛的贡献值(增加气氛的活跃值)。
现在给你一首歌的call表(记录应该如何应援),里面有一部分是0(代表这个动作可以自己随意选择),另一部分是非0整数,表示这个时候有规定的动作要做。
求这首歌能达到的最大气氛活跃值。
初始的气氛活跃值为0,第一个应援动作对气氛无影响。

Input

第1行:组数T(非负整数,1<=T<=10)

第2行:应援动作数n,call表长度m(非负整数,0<=n,m<=100)

第3~3+n行:每行n个整数,表示在第i个动作后接j的气氛贡献值,Wi,j表示当前行第j个。(|Wi,j|<=100)

第4+n行:m个非负整数a[i],表示call表内容。(0<=a[i]<=n)
……

Output

一行,一个整数,表示能达到的最大气氛活跃值。

Sample Input

2
3 5
83 86 77
15 93 35
86 92 49
3 3 3 1 2
5 10
36 11 68 67 29
82 30 62 23 67
35 29 2 22 58
69 67 93 56 11
42 29 73 21 19
0 0 5 0 4 0 0 0 4 0

Sample Output

270
625

Hint

样例解释:
2
3 5
83 86 77
15 93 35
86 92 49
3 3 3 1 2

3 3 3 1 2
没有可以自己随便选的,直接计算得到49+49+86+86=270

5 10
36 11 68 67 29
82 30 62 23 67
35 29 2 22 58
69 67 93 56 11
42 29 73 21 19
0 0 5 0 4 0 0 0 4 0

4 3 5 1 4 1 4 1 4 5
按照这样的顺序来选
可以达到最大值 93+58+42+67+69+67+69+67+93


思路

最近刚接触DP,昨天排位赛中没有推出(大概想了20min没有思路),今天看了ppt的提示,提示是这样说的:

考虑dp[i][j]表示前i个位置,第i个位置选择第j个姿势的最大值

然后就开始写程序,中间递推式需要停下来想一下,最后完成了ac。

所以DP给我的感觉是怎么表示状态很重要,一旦清楚什么参数表示什么自然而然递推式就可以去试着推出来,再加上边界那这题就OK了。
所以问题的关键在于:

构造状态

还是多多练习吧!


代码示例

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

const int maxn=105;

const int inf=0x3f3f3f3f;

int n,m;

int arr[maxn];

int call[maxn][maxn];//存call表 下标1开始

int dp[maxn][maxn];
//dp[i][j]表示第i个位置(i-1个气氛值)选择j动作的最大值

int main()
{
    ios::sync_with_stdio(false);
    int T;
    cin>>T;
    while(T--)
    {
        memset(dp,-inf,sizeof(dp));
        cin>>n>>m;
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                cin>>call[i][j];
            }
        }
        for(int i=1;i<=m;++i){
            cin>>arr[i];
        }

        for(int i=1;i<=n;++i){
            dp[1][i]=0;
        }

        for(int i=2;i<=m;++i){
            if(arr[i-1]){
                if(arr[i]){
                    dp[i][arr[i]]=dp[i-1][arr[i-1]]+call[arr[i-1]][arr[i]];
                    //for(int j=1;j<=n;++j) if(j!=arr[i]) dp[i][j]-=100000;
                }
                else{
                    for(int j=1;j<=n;++j){
                        dp[i][j]=dp[i-1][arr[i-1]]+call[arr[i-1]][j];
                    }
                }
            }
            else{
                if(arr[i]){
                    for(int j=1;j<=n;++j){
                        dp[i][arr[i]]=max(dp[i][arr[i]],dp[i-1][j]+call[j][arr[i]]);
                    }
                }
                else{
                    for(int j=1;j<=n;++j){
                        for(int k=1;k<=n;++k){
                            dp[i][k]=max(dp[i][k],dp[i-1][j]+call[j][k]);
                        }
                    }
                }
            }
        }
        int ans=-inf;
        for(int i=1;i<=n;++i){
            if(dp[m][i]>ans) ans=dp[m][i];
        }
        cout<<ans<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值