道路游戏
Description
小新正在玩一个简单的电脑游戏。
游戏中有一条环形马路,马路上有n个机器人工厂,两个相邻机器人工厂之间由一小段马路连接。小新以某个机器人工厂为起点,按顺时针顺序依次将这n 个机器人工厂编号为1~n,因为马路是环形的,所以第n 个机器人工厂和第1 个机器人工厂是由一段马路连接在一起的。小新将连接机器人工厂的这n段马路也编号为1~n,并规定第i段马路连接第i 个机器人工厂和第i+1 个机器人工厂(1 ≤ i ≤ n-1),第n 段马路连接第n个机器人工厂和第1个机器人工厂。
游戏过程中,每个单位时间内,每段马路上都会出现一些金币,金币的数量会随着时间发生变化,即不同单位时间内同一段马路上出现的金币数量可能是不同的。小新需要机器人的帮助才能收集到马路上的金币。所需的机器人必须在机器人工厂用一些金币来购买,机器人一旦被购买,便会沿着环形马路按顺时针方向一直行走,在每个单位时间内行走一次,即从当前所在的机器人工厂到达相邻的下一个机器人工厂,并将经过的马路上的所有金币收集给小新,例如,小新在i(1 ≤ i ≤ n)号机器人工厂购买了一个机器人,这个机器人会从i 号机器人工厂开始,顺时针在马路上行走,第一次行走会经过i 号马路,到达i+1号机器人工厂(如果i=n,机器人会到达第1个机器人工厂),并将i号马路上的所有金币收集给小新。
游戏中,环形马路上不能同时存在2个或者2个以上的机器人,并且每个机器人最多能够在环形马路上行走p次。小新购买机器人的同时,需要给这个机器人设定行走次数,行走次数可以为1~p 之间的任意整数。当马路上的机器人行走完规定的次数之后会自动消失,小新必须立刻在任意一个机器人工厂中购买一个新的机器人,并给新的机器人设定新的行走次数。
以下是游戏的一些补充说明:
- 游戏从小新第一次购买机器人开始计时。
- 购买机器人和设定机器人的行走次数是瞬间完成的,不需要花费时间。
- 购买机器人和机器人行走是两个独立的过程,机器人行走时不能购买机器人,购买完机器人并且设定机器人行走次数之后机器人才能行走。
- 在同一个机器人工厂购买机器人的花费是相同的,但是在不同机器人工厂购买机器人的花费不一定相同。
- 购买机器人花费的金币,在游戏结束时再从小新收集的金币中扣除,所以在游戏过程中小新不用担心因金币不足,无法购买机器人而导致游戏无法进行。也因为如此,游戏结束后,收集的金币数量可能为负。
现在已知每段马路上每个单位时间内出现的金币数量和在每个机器人工厂购买机器人需要的花费,请你告诉小新,经过m 个单位时间后,扣除购买机器人的花费,小新最多能收集到多少金币。
Analysis
简洁的题面,呵呵。
先随便写一个四维的状态转移方程:
dp[i][j]=max( dp[k][l]+treat(k+1,i,j) )
什么意思呢,令dp[i][j]为i时间在j工厂报废时的最大金币数,那么它一定是由k时间在l工厂报废的状态延伸来的,并且获得了从k+1开始一直走到i到达j工厂的金币。
显然的,l毫无用处,只需要取dp[k][]的最大值,既然如此不如直接舍去第二维。
dp[i]=max( dp[k]+treat(k+1,i,j) )
方程看起来不太好简化,从treat入手。求一段区间的固定的值,这不就是前缀和吗?只不过,这是个二维前缀和,前缀和sum[i][j]的转移方式是从[i-1][j-1]转移而来(我忽然意识到,前缀和不也是一种简单的线性动规吗!!)。
既然如此得到最终方程
dp[i]=max( dp[i-k]+sum[i][j]-sum[i-k][j-k] )
WA90,是不是我写的有问题?原来可能解会出现负数情况,为了取的负解,只能赋无限小memset0x80。还是错,为什么?然后开始乱改,终于发现,原来我在环操作的时候忽视了k可能太大的情况,那么j-k可能+n之后还是负数..多加几个n,就A了。
A了之后我发现,这不科学啊。数据100%满足n,m,p<=1000,我这O(nmp)的算法很明显过不了。是数据太弱的问题,O(^2)算法需要思考思考。
Code O(nmp)
#include <bits/stdc++.h>
int n,m,p,dp[1010],cost[1010],s[1010][1010],gain[1010][1010];
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
std::cin>>n>>m>>p;
for(int i=0;i<n;i++)
for(int j=1;j<=m;j++)
std::cin>>s[j][i];
for(int i=0;i<n;i++)
std::cin>>cost[i];
for(int i=1;i<=m;i++)
for(int j=0;j<n;j++)
gain[i][j]=gain[i-1][(j+n-1)%n]+s[i][j];
memset(dp,0x80,sizeof(dp));
dp[0]=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=std::min(i,p);j++)
for(int k=0;k<n;k++)
dp[i]=std::max(dp[i],dp[i-j]+gain[i][k]-gain[i-j][(k-j+2*n)%n]-cost[(k-j+1+2*n)%n]);
std::cout<<dp[m]<<std::endl;
return 0;
}