大致题意:
有 $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<bits/stdc++.h>
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<<ANS;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。