道路游戏
【题目描述】
游戏中有一条环形马路,马路上有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之间的任意整数。当马路上的机器人行走完规定的次数之后会自动消失,小新必须立刻在任意一个机器人工厂中购买一个新的机器人,并给新的机器人设定新的行走次数。
以下是游戏的一些补充说明:
(1)游戏从小新第一次购买机器人开始计时;
(2)购买机器人和设定机器人的行走次数是瞬间完成的,不需要花费时间;
(3)购买机器人和机器人行走是两个独立的过程,机器人行走时不能购买机器人,购买完机器人并且设定机器人行走次数之后机器人才能行走;
(4)在同一个机器人工厂购买机器人的花费是相同的,但是在不同机器人工厂购买机器人的花费不一定相同;
(5)购买机器人花费的金币,在游戏结束时再从小新收集的金币中扣除,所以在游戏过程中小新不用担心因金币不足,无法购买机器人而导致游戏无法进行。也因为如此,游戏结束后,收集的金币数量可能为负;
现在已知每段马路上每个单位时间内出现的金币数量和在每个机器人工厂购买机器人需要的花费,请你告诉小新,经过m个单位时间后,扣除购买机器人的花费,小新最多能收集到多少金币。
第一行3个正整数,n、m、p,意义如题目所述;
接下来的n行,每行有m个正整数,每两个整数之间用一个空格隔开,其中第i行描述了i号马路上每个单位时间内出现的金币数量(1 ≤ 金币数量 ≤ 100),即第i行的第j(1 ≤ j ≤ m)个数表示第j个单位时间内i号马路上出现的金币数量;
最后一行,有n个整数,每两个整数之间用一个空格隔开,其中第i个数表示在i号机器人工厂购买机器人需要花费的金币数量(1 ≤ 金币数量 ≤ 100)。
共一行,包含1个整数,表示在m个单位时间内,扣除购买机器人花费的金币之后,小新最多能收集到多少金币。
2 3 2
1 2 3
2 3 4
1 2
5
【数据范围及提示】
对于40%的数据,2 ≤ n ≤ 40,1 ≤ m ≤ 40;
对于90%的数据,2 ≤ n ≤ 200,1 ≤ m ≤ 200;
对于100%的数据,2 ≤ n ≤ 1000,1 ≤ m ≤ 1000,1 ≤ p ≤ m。
90分正常DP:
源代码: #include<cstdio> #include<cstring> #include<algorithm> #define INF -1000000000 using namespace std; int n,m,p,f[1001],Sum[1001],i[1001][1001]; int Solve(int Left,int Right,int T) //普通统计机器人能捡到的金币数。 { int Num=0,t=T; //T代表的是起点,故t为现走到的点。 for (int a=Left;a<=Right;a++) //时间区间。 { Num+=i[t][a]; if (t==n) //环形马路。 t=1; else t++; } return Num; } int main() //O(n^4)果断爆掉。 { scanf("%d%d%d",&n,&m,&p); for (int a=1;a<=n;a++) for (int b=1;b<=m;b++) scanf("%d",&i[a][b]); for (int a=1;a<=n;a++) scanf("%d",&Sum[a]); for (int a=1;a<=m;a++) f[a]=INF; //因为结果可能为负,故赋极小值。 for (int a=1;a<=m;a++) //时间。 for (int b=1;b<=n;b++) //机器人的出发点。 { int t=min(a,p); //仔细想想,t是个范围限制,受到最大步数与现实已走步数的制约。 for (int c=1;c<=t;c++) //机器人的步数。 f[a]=max(f[a],f[a-c]-Sum[b]+Solve(a-c+1,a,b)); } printf("%d",f[m]); return 0; }
正解:
源代码: #include<cstdio> #include<algorithm> using namespace std; int n,m,p,f[1001],Sum[1001],i[1001][1001]; int main() //大体思想类似。 { scanf("%d%d%d",&n,&m,&p); for (int a=1;a<=n;a++) for (int b=1;b<=m;b++) scanf("%d",&i[a][b]); for (int a=1;a<=n;a++) scanf("%d",&Sum[a]); for (int a=1;a<=m;a++) f[a]=-1000000000; for (int a=1;a<=m;a++) for (int b=1;b<=n;b++) //枚举a时的终点,注意环形马路。 { int t=min(a,p); int T=b-1; if (!T) T=n; int S=i[T][a]; for (int c=1;c<=t;c++) { f[a]=max(f[a-c]-Sum[T]+S,f[a]); if (T==1) T=n; else T--; S=S+i[T][a-c]; //每次顺带累加。 } } printf("%d",f[m]); return 0; }