[Codeforces Round #635 (div2)]1348E - Phoenix and Berries[dp]



1348E - Phoenix and Berries[ d p dp dp]

time limit per testmemory limit per testinputoutput
2 seconds256 megabytesstandard inputstandard output

Description:

Phoenix is picking berries in his backyard. There are n n n shrubs, and each shrub has a i a_i ai red berries and bi blue berries.

Each basket can contain k k k berries. But, Phoenix has decided that each basket may only contain berries from the same shrub or berries of the same color (red or blue). In other words, all berries in a basket must be from the same shrub or/and have the same color.

For example, if there are two shrubs with 5 5 5 red and 2 2 2 blue berries in the first shrub and 2 2 2 red and 1 1 1 blue berries in the second shrub then Phoenix can fill 2 2 2 baskets of capacity 4 4 4 completely:

  the first basket will contain 3 3 3 red and 1 1 1 blue berries from the first shrub;
  the second basket will contain the 2 2 2 remaining red berries from the first shrub and 2 2 2 red berries from the second shrub.

Help Phoenix determine the maximum number of baskets he can fill completely!

Input

The first line contains two integers n n n and k ( 1 ≤ n , k ≤ 500 ) k (1≤n,k≤500) k(1n,k500) — the number of shrubs and the basket capacity, respectively.
The i i i-th of the next n n n lines contain two integers a i a_i ai and b i b_i bi ( 0 ≤ a i , b i ≤ 1 0 9 ) (0≤a_i,b_i≤10^9) (0ai,bi109) — the number of red and blue berries in the i i i-th shrub, respectively.

Output

Output one integer — the maximum number of baskets that Phoenix can fill completely.

One Example input

2 4
5 2
2 1

One Example output

2

Two Example input

1 5
2 3

Two Example output

1

Three Example input

2 5
2 1
1 3

Three Example output

0

Four Example input

1 2
1000000000 1

Four Example output

1

Hit

The first example is described above.

In the second example, Phoenix can fill one basket fully using all the berries from the first (and only) shrub.

In the third example, Phoenix cannot fill any basket completely because there are less than 5 5 5 berries in each shrub, less than 5 5 5 total red berries, and less than 5 5 5 total blue berries.

In the fourth example, Phoenix can put all the red berries into baskets, leaving an extra blue berry behind.

分析:
参考博客 [动态规划] Codeforces 1348E Phoenix and Berries

题意:
每棵灌木有红色浆果( a i a_i ai)和蓝色浆果( b i b_i bi),每个篮子可以放 k k k 个浆果,
每个篮子有以下两种放法:
a . a. a. 每个篮子只能放来自同一灌木的浆果
b . b. b. 每个篮子只能放同一种颜色的浆果
问最多可以完全装满的最大的篮子数

做法:
可以考虑到 d p [ i ] [ r e d ] [ b l u e ] dp[i][red][blue] dp[i][red][blue]
即到第 i i i 个篮子为止,红色浆果剩余 r e d red red,蓝色浆果剩余 b l u e blue blue,最大满篮数
由于剩余的个数应该 0 ≤ r e d , b l u e < k 0 \leq red, blue< k 0red,blue<k
这样的话时间复杂度 O ( 500 ∗ 500 ∗ 500 ) O(500*500*500) O(500500500),卡一卡过得去
但是空间复杂度 O ( 500 ∗ 500 ∗ 500 ) O(500*500*500) O(500500500) 显然是不可以的
可以考虑将蓝色浆果的略去

d p [ i ] [ j ] dp[i][j] dp[i][j] 为到第 i i i 个灌木为止,剩余 j j j 个红浆果,最大满篮数
假设现在到 i − 1 i-1 i1 为止剩余红浆果为 j j j 个, t s u m = ∑ 1 ≤ q ≤ i − 1 ( a q + b q ) tsum = \sum_{1 \leq q \leq i-1} (a_q+b_q) tsum=1qi1(aq+bq)
i − 1 i-1 i1 为止剩余的 r e d + b l u e = s u m = t s u m − k ∗ d p [ i − 1 ] [ j ] red + blue = sum = tsum - k * dp[i-1][j] red+blue=sum=tsumkdp[i1][j]
i i i 为止,红浆果为 r e d = j + a [ i ] red = j + a[i] red=j+a[i], 蓝浆果为 b l u e = s u m − r e d + b [ i ] = s u m − j + b [ i ] blue = sum-red + b[i] = sum-j + b[i] blue=sumred+b[i]=sumj+b[i]

  1. 先计算直接将 相同颜色的浆果 放进篮子中后的满篮子数
    s r = ( r e d + a [ i ] ) % k = ( j + a [ i ] % k ) sr = (red + a[i]) \% k = (j + a[i] \% k) sr=(red+a[i])%k=(j+a[i]%k),即剩余的红浆果
    d p [ i ] [ s r ] = d p [ i − 1 ] [ j ] + r e d / k + b l u e / k dp[i][sr] = dp[i-1][j] + red / k + blue / k dp[i][sr]=dp[i1][j]+red/k+blue/k
  2. 暴力遍历该灌木的 t t t 个红浆果 和 k − t k - t kt 个蓝浆果放一篮
    l = m a x ( 0 , k − b [ i ] ) , r = m i n ( k , a [ i ] ) l = max(0, k - b[i]), r = min(k, a[i]) l=max(0,kb[i]),r=min(k,a[i]),分别为 t t t 的最小和最大取值
    则到目前为止
    s r = r e d − t , s b = b l u e − ( k − t ) sr = red - t, sb = blue - (k - t) sr=redt,sb=blue(kt),分别为放完混的这一篮后剩余的红、蓝浆果
    d p [ i ] [ s r % k ] = m a x ( d p [ i ] [ s r % k ] , d p [ i − 1 ] [ j ] + 1 + s r / k + s b / k ) dp[i][sr \% k] = max(dp[i][sr \% k], dp[i-1][j] + 1 + sr/k + sb/k) dp[i][sr%k]=max(dp[i][sr%k],dp[i1][j]+1+sr/k+sb/k)

温馨提示:
这里取余最好是定义一个值存着用
不然公式里面取余会 T T T,(是的我就是为了好看T了
当然其实可以再进行压缩一下,利用循环数组
可以发现 d p [ i ] dp[i] dp[i] 只与 d p [ i − 1 ] dp[i-1] dp[i1] 有关,因此其实只要 d p [ 2 ] [ m a x n ] dp[2][maxn] dp[2][maxn] 也就可以了
因此 d p [ 2 ] [ m a x n ] [ m a x n ] dp[2][maxn][maxn] dp[2][maxn][maxn] 貌似也没什么问题 ?

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define max(a,b) ((a)>(b)?(a):(b))
const int maxn = 500 + 5;
const int maxm = 3e6 + 5;
 
int re() {
    int x = 0, f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-f; c=getchar();}
    while(c>='0' && c<='9'){x=(x<<3)+(x<<1)+c-'0'; c=getchar();}
    return x * f;
}
 
ll dp[maxn][maxn]; // 到前 i 颗,红色剩 j
int a[maxn];
int b[maxn];
 
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    memset(dp, -1, sizeof(dp));
    for(int i = 1; i <= n; ++i)
        a[i] = re(), b[i] = re();
    
    int l, r;
    ll tsum = 0, sum, sr, sb;
    dp[0][0] = 0;
    for(int i = 1; i <= n; ++i) {
        for(int j = 0; j < k; ++j) {
            if(dp[i - 1][j] < 0)    continue;
            sum = tsum - k * 1ll * dp[i - 1][j];
            sr = (j + a[i])%k, sb = sum - j + b[i];
            dp[i][sr] = dp[i - 1][j] + (j+a[i])/k + sb/k;
            l = max(0, k - b[i]), r = min(k, a[i]);
            for(int t = l; t <= r; ++t) { // 第i个取几个红色混放
                sr = (j - t + a[i])%k, sb = sum - j + b[i] - (k - t); // 剩余红色, 蓝色
                dp[i][sr] = max(dp[i][sr], dp[i-1][j] + 1 + (j - t + a[i])/k + sb/k);
            }
        }
        tsum += (a[i] + b[i]) * 1ll;
    }
    ll ans = 0;
    for(int i = 0; i < k; ++i)
        ans = max(ans, dp[n][i]);
    printf("%lld\n", ans);
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值