Codeforces Round #274 (Div. 2) E:Riding in a Lift DP + 前缀优化

题意:

n,a,b,k(2?≤?n?≤?5000,1?≤?k?≤?5000,1?≤?a,?b?≤?n,a?≠?b).四个数.1到n的数,顺序排列,其实位置人在a位置而中心位置在b,人每次只能走一个点走动的距离必须小于|b?a|,人走k步之后停止,问人一共有多少种走法.

分析:

开始很容易想到一个深度优先搜索实现递归方法dfs(a, k)但k变为0就到达搜索底部,这样时间复杂度是O(nk)显然非常不好.

然后可以想到会有重算的情况,就可以加一个记忆优化把算过的(a,k)的二元组都保存下来.这样处理之后复杂度是O(n2?k),递归调用的话本来效率就不高,本地跑一个大数都要十多秒..

然后把记忆优化改为递推的形式:设dp[i][j]为第i个数剩下有j步的走法总数.有这样的状态转移方程:

dp[i][j]=∑k = max(1,i?|b?a|+1)min(n,i+|b?a|?1)dp[k][j?1]

这样的复杂度是O(k?n2)的显然超时,只是相比记忆优化的递归消耗要快了些.这里注意到是求区间和的最内层循环耗费了时间,就可以通过解决区间和的前缀数组来解决.其所称谓的前缀数组就是令:

s[k] = ∑i = 1kp[i][j?1]

这样就可以在O(1)的时间算出区间[x, y]的和为:s[y]?s[x?1]加上前缀数组的优化,整个算法复杂度就是O(n*k)显然可以承受了.

我以为已经理论ac了然而no,这里还有一个困扰了很久的点就是答案巨大需要对1000000007取余.取于的姿势很容易错.*这里要明白取于和取模的区别,计算机叫取模,数学叫取余,在负数的时候会有差别,甚至不太系统定义不一样,因为负数参与取余压根就没有多少实际意义,所以在程序中没有理由对负数和减法取余.对加法取余数是为了防止爆longlong,对减法毫无意义.

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, a, b, kk;
const LL mod = 1000000007;
LL dp[5009], s[5009];

int main(void)
{
    while (cin >> n >> a >> b >> kk) {
        for (int i = 0; i <= n; i++) dp[i] = i;
        for (int j = 1; j <= kk; j++) {
            for (int i = 1; i <= n; i++ ){
                s[i] = ( ( dp[i - 1] - dp[max(1, i - (abs(b - i) - 1)) - 1] ) % mod+ (dp[min(n, i + abs(b - i) - 1)]- dp[i])%mod )%mod;
            }
            for (int i = 1; i <= n;i ++)  dp[i] = dp[i - 1]  + s[i];

        }
        cout << s[a] % mod << endl;
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-23 05:10:58

Codeforces Round #274 (Div. 2) E:Riding in a Lift DP + 前缀优化的相关文章

Codeforces Round #274 Div.1 C Riding in a Lift --DP

题意:给定n个楼层,初始在a层,b层不可停留,每次选一个楼层x,当|x-now| < |x-b| 且 x != now 时可达(now表示当前位置),此时记录下x到序列中,走k步,最后问有多少种可能的数的序列. 解法: 定义:      dp[i][j] 表示第i步在j楼的不同序列的个数 转移方程: 当j<b时, 那么dp[i][j] += dp[i-1][0~(j与b的中点(以下))] 当j>b时, 那么dp[i][j] += dp[i-1][(j与b的中点(以下))~n] 由于dp[

Codeforces Round #274 (Div. 2) E. Riding in a Lift(DP)

Imagine that you are in a building that has exactly n floors. You can move between the floors in a lift. Let's number the floors from bottom to top with integers from 1 to n. Now you're on the floor number a. You are very bored, so you want to take t

Codeforces Round #267 (Div. 2) C. George and Job(DP)补题

Codeforces Round #267 (Div. 2) C. George and Job题目链接请点击~ The new ITone 6 has been released recently and George got really keen to buy it. Unfortunately, he didn't have enough money, so George was going to work as a programmer. Now he faced the follow

Codeforces Round #433 (Div. 1) D. Michael and Charging Stations(dp)

题目链接:Codeforces Round #433 (Div. 1) D. Michael and Charging Stations 题意: 一个人每天要加油,1种为1000,1种为2000,如果付全额,会得到10%的回扣放在卡上. 如果卡上有剩余的回扣,可以拿来抵现金.问n天最少需要花多少钱. 题解: 很直观的一个dp就是考虑dp[i][j],表示第i天卡上剩余回扣为j的最小花费. 将所有的数除以100后,j其实是小于40的,严格的说是小于30,官方题解有个证明. 因为卡上不可能积累很多的

Codeforces Round #419 (Div. 2) E. Karen and Supermarket(树形DP)

题目链接:Codeforces Round #419 (Div. 2) E. Karen and Supermarket 题意: 有n件物品,每个物品有一个价格,和一个使用优惠券的价格,不过这个优惠券有一个限制,必须要在第x个使用后才可以使用.现在有m的钱,问最多能买多少个物品. 题解: 每个优惠券都只与一个券有关,所以根据这个关系就可以构成一棵树. 考虑树形dp,dp[i][j][k(0|1)]表示第i个节点所构成的子树中买了j个物品,使用优惠券和不使用优惠券的最少钱. 转移方程看代码详细解释

codeforces水题100道 第八题 Codeforces Round #274 (Div. 2) A. Expression (math)

题目链接:http://www.codeforces.com/problemset/problem/479/A题意:给你三个数a,b,c,使用+,*,()使得表达式的值最大.C++代码: #include <iostream> using namespace std; int a, b, c, ans; int main() { cin >> a >> b >> c; int t = max(a+b, a*b); ans = max(t + c, t * c

Codeforces Round #274 (Div. 2) B. Towers

As you know, all the kids in Berland love playing with cubes. Little Petya has n towers consisting of cubes of the same size. Tower with number i consists of ai cubes stacked one on top of the other. Petya defines the instability of a set of towers a

Codeforces Round #274 (Div. 2) C. Exams

Student Valera is an undergraduate student at the University. His end of term exams are approaching and he is to pass exactly n exams. Valera is a smart guy, so he will be able to pass any exam he takes on his first try. Besides, he can take several

Codeforces Round #274 (Div. 2)

A. Expression 题意:给出a,b,c,给出"+","*",“()”在这三个数中任意放置这三个符号,求最大值 直接枚举6种情况就可以了,自己写的时候是挨个比找的最大值,后来发现别人的题解里面,直接将这6个值排序取最后一个数就可以了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include