TopCoder SRM450C: RowGame 题解

看到k这么大,我的思维立刻就往什么dp+矩乘或者是倍增上走
dp+矩乘看上去挺靠谱的,但是题目要求任意时刻权值非负,那么就得再加一维来控制当前的权值,而当前权值的范围是很大的,这样会超时
于是考虑仔细观察此题的性质
我们可以设想:如果 k i n f 那么我一定会想办法走到这个序列的最长子串上然后来回摆动
然而并不是能一下子走到最长子串上的,如果中间有一个比较大的负数,那我就要在它之前的一个区间内来回摆动,直到我的和积蓄到超过这个负数的绝对值,我就能跨过去,前往下一个和更大的区间
所以我的路线一定是长成这样的
这里写图片描述
我们称一个不停的往返的区间为一个块,那么我们可以发现一个性质,这些块的权值一定是从左向右递增的,否则我就可以留在左边的块不向右走
这个性质就带来了一个另一个很好的性质:我一定会在前面“浪费”尽可能少的步数,因为走到后面之后我的收获更多,也就是说,一旦我的权值能够到下一个区间我就会立刻过去,不会再在当前的小区间上停留
所以我们可以看出,步数是第一关键字,步数相同的情况下权值和是第二关键字
考虑dp,dp1[i]表示走到以i结尾的块所需要的最少步数,dp2[i]表示以最少步数走到以i结尾的块的前提下,权值和最大是多少
转移是很简单的,只要枚举之前的一个位置j,然后计算一下打几个圈就能够上到i,转移转移就好了
最后对于每个位置,可以把剩下的k-dp1[i]用在当前块上打圈圈,加一下取个max就好
这个dp的模型还是相当隐蔽

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);

LL sum[148];int n;

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

class RowGame
{
    int n,N,a[148];LL sum[148];
    int left[148];LL maxsum[148],maxn;
    LL dp1[148],dp2[148];
    inline void check_max(int cur,int last,LL t)
    {
        if (dp1[cur]==-1 || dp1[cur]>dp1[last]+t+1) 
        {
            dp1[cur]=dp1[last]+t+1;
            dp2[cur]=dp2[last]+t*maxsum[last]+sum[cur]-sum[left[last]-1];
        }
        else if (dp1[cur]==dp1[last]+t+1)
        {
            dp2[cur]=max(dp2[cur],dp2[last]+t*maxsum[last]+sum[cur]-sum[left[last]-1]);
        }
    }
    public:
        inline LL score(vector<int> board,int k)
        {
            n=int(board.size());int i,j;
            memset(sum,0,sizeof(sum));
            for (i=1;i<=n;i++) a[i]=board[i-1],sum[i]=sum[i-1]+a[i];
            memset(left,-1,sizeof(left));memset(maxsum,-1,sizeof(maxsum));
            for (i=1;i<=n;i++)
                for (j=1;j<=i;j++)
                    if (sum[i]-sum[j-1]>maxsum[i]) maxsum[i]=sum[i]-sum[j-1],left[i]=j;
            maxn=-1;
            for (i=1;i<=n;i++) if (maxsum[i]>maxn) maxn=maxsum[i],N=i;
            if (maxn==-1) return 0;
            memset(dp1,-1,sizeof(dp1));
            for (i=1;i<=N;i++)
            {
                if (left[i]==-1) continue;
                if (sum[i]>=0) {dp1[i]=1;dp2[i]=sum[i];continue;}
                for (j=1;j<=i-1;j++)
                    if (dp1[j]!=-1 && left[j]!=-1)
                    {
                        LL cursum=dp2[j],add=sum[i]-sum[left[j]-1],delta=cursum+add;
                        LL tt=0;
                        if (delta<0) {if (!maxsum[j]) continue;tt=((-delta)%maxsum[j]==0?(-delta)/maxsum[j]:(-delta)/maxsum[j]+1);}
                        if (tt%2==0) tt++;
                        check_max(i,j,tt);
                    }           
            }
            LL res=0;
            for (i=1;i<=N;i++)
                if (dp1[i]!=-1 && dp1[i]<k)
                {
                    int lft=k-dp1[i];
                    res=max(res,dp2[i]+maxsum[i]*lft);
                }
            return res;
        }
};
/*---Debug Part---*/
/*
int main ()
{
    RowGame A;int nn;
    while (scanf("%d",&nn)!=EOF)
    {
        int x;vector<int> vv;
        while (nn--)
        {
            x=getint();
            vv.pb(x);
        }
        x=getint();
        cout<<A.score(vv,x)<<endl;
    }
    return 0;
}*/

猜你喜欢

转载自blog.csdn.net/IcePrincess_1968/article/details/80386210