Codeforces Round #638 (Div. 2) E. Phoenix and Berries dp
题意
有n个灌木丛,每个灌木丛有(a_i)个红色浆果,(b_i)个蓝色浆果。一个篮子可以装k个浆果,并且每个篮子只能承载要么是同一个灌木丛摘下来的浆果,要么是同一颜色的浆果,问最多装满多少个篮子(n<=500,k<=500)
分析
首先数据很DP,往DP靠一靠。最关键的是要发现每个灌木丛最多只能产出一个混色的篮子(因为假如你产生了多个混色篮子,那么他们可以经过互相交换,最后剩下一个混色的篮子),能想到这一步,这一题也就好做了。
要想知道这个混色篮子有几个红的几个蓝的,只要枚举就行了,那么组成完混色篮子后,同色的能产生多少个呢?我们只需要记录一下状态即可,可以只记录红的或者蓝的,另外一个可以O(1)算出来(DP常用的手段),也就是说用一维扫1--n个篮子,一维记录红的或者蓝的(我们这里选红的)的剩余数量,这里的数量一定是小于k的,因为如果大于k直接组成篮子加进dp值就行了。
所以我们设置状态为dp[i][j]前i个篮子剩下j个红的时候的最大篮子数量
我们设前i-1个的红蓝和为sum,此时红色的剩余数量为(j+a[i]),蓝色的剩余数量为(sum-dp[i-1][j]*k-j+b[i])
所有当能组成混色蓝色时,我们设有红的z个,那么蓝的就有k-z个,转移为
(dp[i][(j+a[i]-z)\%k]=max(dp[i][(j+a[i]-z)\%k],dp[i-1][j]+1+(j+a[i]-z)/k+(sum-j-k*(dp[i-1][j])-(k-z)+b[i])/k))
不组成混色篮子的时候转移为
(dp[i][(j+a[i])\%k]=dp[i-1][j]+(j+a[i])/k+(sum-j-dp[i-1][j]*k+b[i])/k;)
能和不能状态相同的时候取最大即可
复杂度(O(nk^2))有点极限,不过1.25e8,2秒也差不多
ps:刚开始a[i],b[i]用的long long,cf的机器好像是32位的,直接卡掉了,改成了int就过了。。下次碰到这种情况可以考虑一下快读,偷懒fst就凉了
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define F first
#define S second
#define mkp make_pair
#define pii pair<int,int>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=5e3+4;
ll dp[maxn][maxn];
int a[maxn],b[maxn];
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
memset(dp,-1,sizeof(dp));
dp[0][0]=0;
ll sum=0;
for(int i=1;i<=n;i++){
for(int j=0;j<k;j++){
if(dp[i-1][j]==-1)continue;
dp[i][(j+a[i])%k]=dp[i-1][j]+(j+a[i])/k+(sum-j-dp[i-1][j]*k+b[i])/k;
for(int z=0;z<k;z++){
if(a[i]>=z&&b[i]>=(k-z))
dp[i][(j+a[i]-z)%k]=max(dp[i][(j+a[i]-z)%k],dp[i-1][j]+1+(j+a[i]-z)/k+(sum-j-k*(dp[i-1][j])-(k-z)+b[i])/k);
}
}
sum+=a[i]+b[i];
}
/*for(int i=1;i<=n;i++){
for(int j=0;j<k;j++){
cout<<dp[i][j]<<" ";
}
cout<<endl;
}*/
ll ans=0;
for(int i=0;i<k;i++)ans=max(ans,dp[n][i]);
printf("%lld
",ans);
return 0;
}