[Noip 2009 普及组 T4] [Luogu P1070] 道路游戏

一道很迷的\(dp\)

感谢大爱无疆的sy

开始的时候采用的二维状态但是并不对)

然后把状态改成一维\(AC\)

\(Solution\)

状态:开始想的是\(f[i][j]\),表示到达\(i\)时间时在\(j\)点处的最多金币数。

转移方程设置为:\(f[i][j]=max\{f[i-1][j-k]+sum-val[j-k]\}\)其中\(sum\)表示从\(j-k\)走到\(j\)所赚的金币数

但这样是不对的,因为题目中:

也就是上一个机器人消失的地方并不一定要买下一个机器人,而是可以换另一个地方买。

如果硬要设计地点的话,应该将\(f[i-1][j-k]\)变为\(max\{f[i-1][1,2,……n]\}\)

这样时间复杂度是\(O(n^4)\)的,想过题可能在想\(peach\)

经过思考,我们发现地点是没什么用的,所以将第二维去掉,\(f[i]\)表示时间为\(i\)时能获得的最大金币数;

转移方程:\(f[i]=max\{f[i-k]+sum-val[d],d=(j-k)\%n+n\}\)

表示在\(d\)点购买的机器人从\(d\)点走到\(j\)点赚取的金币数,枚举\(j、d\),取得\(f[i]\)时的最大值

\(sum\)表示从\(d\)走到\(j\)获得的金币数,最简单的方法,暴力求解:

int pay(int a,int b,int x,int y) {
    int rtn=0,j=a;
    for(int i=x+1;i<=y;i++) {
        rtn+=cost[j][i];
        j++;
        if(j>n) j=1;
    }
    return rtn;
}
//表示从a走到b,时间从x变成y所获得的金币数

\(Code\):

#include

using namespace std;

inline int read() {
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int n,m,p; 
int cost[1010][1010];
int val[1010];
int f[1010];

int pay(int a,int b,int x,int y) {
    int rtn=0,j=a;
    for(int i=x+1;i<=y;i++) {
        rtn+=cost[j][i];
        j++;
        if(j>n) j=1;
    }
    return rtn;
}

int main() {
    n=read();
    m=read();
    p=read();
    for(int i=1;i<=n;++i) 
        for(int j=1;j<=m;++j) 
            cost[i][j]=read(); 
    for(int i=1;i<=n;++i) 
        val[i]=read();
    memset(f,0x9f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=m;++i) { // time i
        for(int j=1;j<=n;++j) { // node j
            for(int k=1;k<=p&&k<=i;++k) { // k
                int d=j-k; 
                if(d<=0) 
                    d=d%n+n;
                int sum=pay(d,j,i-k,i);
                f[i]=max(f[i],f[i-k]+sum-val[d]);
            }
        }
    }
    printf("%d",f[m]);
    return 0;
}

这样会\(TLE\),求\(pay\)实在是太慢了。

因为\(k\)是从小到大枚举的,也就是从只从\(j\)往后退一步,到往后退\(p\)步,赚取的金币数刚好是可以累加的。

所以定义一个\(sum=0\)每次枚举\(k\)时,\(sum+=cost[j-k][i-k+1]\)

#include

using namespace std;

inline int read() {
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int n,m,p; 
int cost[1010][1010];
int val[1010];
int f[1010];

int main() {
    n=read();
    m=read();
    p=read();
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=m;j++) 
            cost[i][j]=read();
    for(int i=1;i<=n;i++) 
        val[i]=read();
    memset(f,0x9f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=m;i++) { // time i
        for(int j=1;j<=n;j++) { // node j
            int sum=0;
            for(int k=1;k<=p&&k<=i;k++) { // zoule k
                int d=j-k; 
                if(d<=0) 
                    d=d%n+n;
                sum+=cost[d][i-k+1];
                f[i]=max(f[i],f[i-k]+sum-val[d]);
            }
        }
    }
    printf("%d",f[m]);
    return 0;
}

你可能感兴趣的:([Noip 2009 普及组 T4] [Luogu P1070] 道路游戏)