dp cf 1700 最近几天的刷题

C. Number of Ways

这个题目的意思是,把这个n的序列分成三个连续的部分,要求这三个部分的和是一样的。问这种划分的方法有多少种。

这个题目和之前写过的数字划分有点像,这个就是要先进行前缀和的处理,然后找到s/3 和 2*s/3 这两个位置。

因为这个有负数,所以有可能出现,2*s/3 的位置在 s/3 的位置之后,所以这个时候就需要进行处理。

我们每一个 s/3 去找到 2*s/3 的所有合法位置即可。

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 10;
ll a[maxn], sum[maxn];
int x[maxn], y[maxn];

int main()
{
    int n;
    ll S = 0;
    scanf("%d", &n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld", &a[i]);
        sum[i] = sum[i - 1] + a[i];
        S += a[i];
    }
    if (S % 3 != 0) {
        printf("0\n");
        return 0;
    }
    S /= 3;
    int tot = 0, cnt = 0;
    for(int i=1;i<n;i++)
    {
        if (sum[i] == S) x[tot++] = i;
        if (sum[i] == 2 * S) y[cnt++] = i;
    }
    if(tot==0||cnt==0)
    {
        printf("0\n");
        return 0;
    }
    ll ans = 0;
    for(int i=0;i<tot;i++)
    {
        ll len = upper_bound(y, y + cnt, x[i]) - y;
        ans += (cnt - len);
    }
    printf("%lld\n", ans);
    return 0;
}
C

 

D. Flowers

这个题目是一个很简单的线性dp。

题目大意:土拨鼠吃花,对于白花,它只吃数量恰好为k这么多的白花,不然就不吃,它给你一个区间从a到b,表示花的数量从a到b,求土拨鼠吃花的方案数。

这个和之前寒假回来后的选拔赛的dp是一样的,因为这个和顺序也有关,所以要先枚举背包容量,然后再去枚举种类,如果和放入背包的顺序无关,就是先枚举种类再去枚举数量。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define inf 0x3f3ff3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const ll mod = 1e9 + 7;
ll dp[maxn];
ll sum[maxn];

int main()
{
    int t, k;
    scanf("%d%d", &t, &k);
    dp[0] = 1;
    for (int i = 1; i <= maxn; i++) {
        if (i >= k) {
            dp[i] = dp[i - 1] + dp[i - k];
            dp[i] %= mod;
        }
        else {
            dp[i] = dp[i - 1];
            dp[i] %= mod;
        }
    }
    for(int i=1;i<=maxn;i++)
    {
        sum[i] = sum[i - 1] + dp[i];
        sum[i] %= mod;
    }
    while(t--)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        ll ans = (sum[b] - sum[a - 1] + mod) % mod;
        printf("%lld\n", ans);
    }
    return 0;
}
D

 

A. DZY Loves Sequences

题目大意:您的任务是找到a的最长子段,这样可以从子段中最多更改一个数字(将一个数字更改为您想要的任何整数),以使子段严格增加。

这个就是处理一下 对于第 i 个值,求出以 i 为起点的递增序列的长度,以 i 为终点的递增序列长度。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define inf 0x3f3ff3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
ll a[maxn], dp1[maxn], dp2[maxn];

int main()
{
    int n;
    scanf("%d", &n);
    a[0] = -inf;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        dp1[i] = 1;
        dp2[i] = 1;
    }
    ll mx = 0;
    for (int i = 2; i <= n; i++) {
        if (a[i] > a[i - 1]) dp1[i] = dp1[i - 1] + 1;
        mx = max(dp1[i], mx);
    }
    for (int i = n - 1; i >= 1; i--) {
        if (a[i] < a[i + 1]) dp2[i] = dp2[i + 1] + 1;
        //printf("dp2[%d]=%lld\n", i, dp2[i]);
    }
    ll ans = 1;
    for(int i=1;i<=n;i++)
    {
        if (a[i - 1] <= a[i + 1] - 2) ans = max(ans, dp1[i - 1] + dp2[i + 1] + 1);
    }
    if (mx < n) mx++;
    ans = max(ans, mx);
    printf("%lld\n", ans);
    return 0;
}
A

 

C. George and Job

这个题目我觉得有点难,就是找到一个长度恰好为m的k个区间,然后这k个区间求和,使得和最大。

这个n,m,k都比较小,所以可以定义一个二维的,这个和之前的奶牛的题目有点类似 https://www.cnblogs.com/EchoZQN/p/11043552.html

这个是lj给了我这个dp状态的定义,我才写出来转移方程的 

dp[i][j] 表示前面 i 个数,选了j个区间的最大和。

所以这个转移方程就是 如果我们选了第i个数 那么  dp[i][j]=max(dp[i-m][j-1],dp[i][j])

如果我们不选第 i 个数 那么 dp[i][j]=max(dp[i][j],dp[i-1][j])

这个都写出来了,就很好写了。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define inf 0x3f3ff3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
ll sum[maxn], a[maxn], dp[5005][5005];

int main()
{
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        sum[i] = sum[i - 1] + a[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=k;j++)
        {
            if (i >= m) {
                dp[i][j] = max(dp[i - m][j - 1] + sum[i] - sum[i - m], dp[i][j]);
                dp[i][j] = max(dp[i - 1][j], dp[i][j]);
            }
            else dp[i][j] = dp[i - 1][j];
        }
    }
    printf("%lld\n", dp[n][k]);
    return 0;
}
C

 

B. Color the Fence

这个题目不是很难,但是需要想一想。

首先,我们优先考虑这个数的长度,然后再考虑这个数的大小。

所以我们可以先由这个最小的数求出长度,然后再去找这个长度如果不整除,那就尽量选更大的。

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
int b[maxn];
int a[15];

int main()
{
    int V;
    scanf("%d", &V);
    int mn = inf;
    for (int i = 1; i <= 9; i++) {
        scanf("%d", &a[i]);
        mn = min(mn, a[i]);
    }
    if (V < mn) {
        printf("-1\n");
        return 0;
    }
    //printf("mn=%d\n", mn);
    int tot = 0;
    int len = V / mn;
    int M = V % mn;
    int mx = mn + M;
    while(len--)
    {
        //printf("len=%d mx=%d\n", len, mx);
        int flag = 0;
        for(int i=9;i>=1;i--)
        {
            if(a[i]<=mx)
            {
                flag = i;
                b[tot++] = i;
                break;
            }
        }
        mx -= (a[flag] - mn);
    }
    for (int i = 0; i < tot; i++) printf("%d", b[i]);
    printf("\n");
    return 0;
}
B

 

posted @ 2019-06-25 15:50  EchoZQN  阅读(161)  评论(0编辑  收藏  举报