[SCU 4510] TaoSama与煎饼 (序列DP+状态优化)

44 篇文章 0 订阅

SCU - 4510

一条长度为 N的道路,其中每个点有个权值
有 M个道具,能使煎饼向前跳跃1、2、3或4步
保证所有道具使用完时,煎饼落在 N位置
求到 N位置的一条路径,使得煎饼沿路获得的权值和最大


首先朴素的想法是用 dp[i][m1][m2][m3][m4]
表示煎饼在 i位置,使用 +1、+2、+3、+4的道具数目
分别为 m1、m2、m3、m4所获得的最大权值

但是显然这样状态爆炸,所以我们可以削减一维状态
由于我比较蠢,所以我的做法并不高明,但是因为数据更蠢,所以我过了
可以把 m1这一维去掉,这是由于 i,m2,m3,m4可以计算出 m1
然后枚举当前要使用的道具,根据这几个状态进行转移即可
然后这样会 MLE,要使用滚动数组,我因为滚动时memset放错位置还 WA了两发
时间复杂度 O(N*K^3),其中 K为某种道具的最大个数

其实正解的思想是一样的,只不过他削减了 i这一维,
因为知道了 m1,m2,m3,m4同样可以算出 i
这样时间复杂度就变为了 O(K^4)

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) (a*a)

const int maxn=400;
int N,M;
int inpt[maxn];
int dp[5][41][41][41];
int mcnt[5];

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    int T;
    scanf("%d", &T);
    for(int ck=1; ck<=T; ck++)
    {
        int mtot=0;
        MST(dp,-1);
        CLR(mcnt);
        scanf("%d%d", &N, &M);
        for(int i=1; i<=N; i++) scanf("%d", &inpt[i]);
        for(int i=1; i<=M; i++) 
        {
            int x;
            scanf("%d", &x);
            mcnt[x]++;
            mtot++;
        }
        dp[1][0][0][0]=0;
        for(int i=1; i<N; i++)
        {
            for(int m2=0; m2<=mcnt[2]; m2++)
            {
                for(int m3=0; m3<=mcnt[3]; m3++)
                {
                    for(int m4=0; m4<=mcnt[4]; m4++)
                    {
                        if(dp[i%5][m2][m3][m4]==-1) continue;
//                      if(2*m2+3*m3+4*m4+1>i) break;
                        int m1=i-2*m2-3*m3-4*m4-1;
                        if(m1<0 || m1>mcnt[1]) continue;
                        int now=dp[i%5][m2][m3][m4]+inpt[i];
                        bool nt1=0,nt2=0,nt3=0,nt4=0;
                        if(m1<mcnt[1]) dp[(i+1)%5][m2][m3][m4]  =max(dp[(i+1)%5][m2][m3][m4],   now), nt1=1;
                        if(m2<mcnt[2]) dp[(i+2)%5][m2+1][m3][m4]=max(dp[(i+2)%5][m2+1][m3][m4], now), nt2=1;
                        if(m3<mcnt[3]) dp[(i+3)%5][m2][m3+1][m4]=max(dp[(i+3)%5][m2][m3+1][m4], now), nt3=1;
                        if(m4<mcnt[4]) dp[(i+4)%5][m2][m3][m4+1]=max(dp[(i+4)%5][m2][m3][m4+1], now), nt4=1;
//                      printf("%d %d %d %d %d %d\n", i, m1, m2, m3, m4, now);
//                      if(nt1) puts("dp-1");
//                      if(nt2) puts("dp-2");
//                      if(nt3) puts("dp-3");
                    }
                }
            }
            MST(dp[i%5], -1);
        }
        printf("%d\n", inpt[N]+dp[N%5][mcnt[2]][mcnt[3]][mcnt[4]]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值