codeforces1348EPhoenix and Berries简单DP

4 篇文章 0 订阅

codeforces1348EPhoenix and Berries题解

题面链接

https://codeforces.ml/contest/1348/problem/E

题意

有n个菜园子,菜园子i有ai个红莓,bi个蓝莓。一个篮子装k个莓就满了,求最多装满几个篮子,每个篮子至少满足下面条件之一:
1.篮子里的莓颜色相同。
2.篮子的莓来自同一个菜园子。
给定n,k和a,b序列,输出最大装满的篮子数。

思路

妈的这道题就是个简单的O(nkk)的暴力DP。我想多了。
我开始的复杂想法:
设一共有sum1个红莓和sum2个蓝莓,显然所有篮子都是情况1的话肯定能装满 s u m 1 / k + s u m 2 / k sum1/k+sum2/k sum1/k+sum2/k个篮子。
而无论如何最多放 ( s u m 1 + s u m 2 ) / k (sum1+sum2)/k (sum1+sum2)/k个篮子,两者可能相等,也可能差1.
令r1=sum1%k,r2=sum2%k。如果r1+r2>=k的话就有希望用某些篮子放情况2的,这样多挤出来一个满的篮子。
如果想找一个菜园的两种颜色拼出一个满篮子,那么这个菜园i必须满足 a i > = k − r 2 , b i > = k − r 1 , a i + b i > = k a_i>=k-r_2,b_i>=k-r1,a_i+b_i>=k ai>=kr2,bi>=kr1,ai+bi>=k这三个条件。
如果没有菜园满足,那说明只靠一个菜园挤是挤不出多一个的。
而这样的话每个菜园至少有一种莓是小于等于k的,就能dp了。
dp[i][j]表示前i个菜园每个菜园取0或k个莓(不是0,k没有意义),剩下所有莓都按情况1办,剩的红梅能否凑出模k余数是j这种情况能否发生。
转移的话只要枚举本菜园取的红莓数量即可。因为至少一种莓小于k所以枚举这个取了几个就行。 d p [ i ] [ j ] ∣ = d p [ i − 1 ] [ ( j + x ) % k ] i : n j : k x : k dp[i][j]|=dp[i-1][(j+x)\%k]\\i:n\\j:k\\x:k dp[i][j]=dp[i1][(j+x)%k]i:nj:kx:k
复杂度nkk。
最后找最小的可能出现的j就行。
后来:
然而仔细想想发现不用这么复杂,直接dp就行。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int NN=510;
int a[NN],b[NN];
int dp[NN][NN];
int main(){
    int n,k;scanf("%d%d",&n,&k);
    long long sum1=0,sum2=0;
    for(int i=1;i<=n;i++){
        scanf("%d%d",a+i,b+i);
        sum1+=a[i];
        sum2+=b[i];
    }
    int r1=sum1%k;
    int r2=sum2%k;
    if(r1+r2<k){
        printf("%lld\n",sum1/k+sum2/k);
        return 0;
    }
    dp[0][r1]=1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<k;j++){
            dp[i][j]=dp[i-1][j];
            if(dp[i][j])continue;
            for(int x=max(0,k-b[i]);x<=min(a[i],k);x++){
                dp[i][j]+=dp[i-1][(j+x)%k];
                if(dp[i][j]==1)break;
            }
        }
    }
    int res;
    for(res=0;res<k;res++){
        if(dp[n][res]){
            break;
        }
    }
    if(r1+r2-res>=k){
        printf("%lld\n",(sum1+sum2)/k);
    }
    else{
        printf("%lld\n",(sum1+sum2)/k-1);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值