Phoenix and Berries -1348E

大致题意:

有 $n$ 棵灌木,第 $i$ 棵灌木有 $a_i$ 个红果实 和 $b_i$ 个蓝果实,现在要求把这些果实装到篮子中,每个篮子的容量为 $k$,且一个篮子内的果实要么都属于同一种颜色,否则都必须属于同一棵灌木,问这 n 棵果实最多可以装满多少个篮子?

分析:

一颗果实的归属有两类

$A$. 双色 红蓝 篮子;
$B$. 单色 红 或 蓝 篮子;

首先一棵灌木填满多个 $A$ 篮,和填满 $0~or~1$ 个 $A$ 篮 + 若干个 $B$ 篮是等价的,所以每棵灌木我们可以枚举填满 $0~or~1$ 个 $A$ 篮的情况 ,剩下的果实全部归属 $B$ 类时,能填满最多的篮子数;枚举状态求最值,可以想到动态规划;

初步容易设 $dp[i][j][z]$ 表示到第 $i$ 棵灌木,余出 $j$ 个红果实和 $z$ 个蓝果实 的情况下,能填满的篮子数,正确性肯定,但 $500$ 的上界,按照这个方程转移肯定时限不够;
再想一想,$j$ 和 $z$ 之间是有联系的,设 $sum$ 为 前 $i$ 棵灌木的果实总数,则 $dp[i][j][z]$ 需要满足:
$$j+z+dp[i][j][z]*k=sum$$

则当你枚举了 $j$,$z$ 的值时确定的,所以可以降维,用 $dp[i][j]$ 表示 到第 $i$ 个篮子,余出 $j$ 个红果实,能填满的篮子数;这样子余出的蓝果实数量 $z = sum-dp[i][j]*k-j$;
$O(n^2)$ 枚举 $dp[i][j]$ ,然后内部再嵌套一层循环枚举填满一个 $A$ 类篮子的情况 ( 枚举一种果实的数量即可,因为两种果实的和固定为 $k$ ) ,预计复杂度 是 $O(n^3)$ ,满足时限;

代码:

#include
using namespace std;
#define ll long long
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define frep(i,a,b) for(int i=a;i>=b;i--)
const int N = 500+10;
ll dp[N][N],n,k,sum;
int main()
{
    //freopen("1.txt","r",stdin);
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    cin>>n>>k;
    memset(dp,-1,sizeof dp);
    dp[0][0]=0;
    rep(i,1,n)
    {
        ll a,b;cin>>a>>b;
        rep(j,0,k-1)if(dp[i-1][j]!=-1)
        {
            ll lft=sum-dp[i-1][j]*k;  
            ll pa=j,pb=lft-j;
            //pa:余出红果实的数量
            //pb:余出蓝果实的数量
            
            rep(z,0,min(k,a))if(z+b>=k) //枚举A类篮子的状态
            {
                //填满这一个A类篮,用了z颗红果实和k-z颗蓝果实
                //第i棵灌木余出a-z颗红果实和b-(k-z)颗蓝果实
                ll na=(a-z+pa); 
                ll nb=(b-(k-z)+pb);
                //na:新余出红果实的数量
                //nb:新余出蓝果实的数量
                
                dp[i][na%k]=max(dp[i][na%k],dp[i-1][j]+1+na/k+nb/k);
            }
            //这里考虑不填A类篮的情况,即所有的果实都属于B类
            dp[i][(pa+a)%k]=max(dp[i][(pa+a)%k],dp[i-1][j]+(pa+a)/k+(pb+b)/k);
        }
        sum+=a+b; //更新果实总数前缀和
    }
    ll ANS=0;
    rep(i,0,k-1) ANS=max(dp[n][i],ANS);
    cout<

你可能感兴趣的:(dp)