Codeforces 1398D - Colored Rectangles (dp)
题意
给定三个集合\(\{R\},\{G\},\{B\}\)
要求每次任选两个集合并分别取出一个数字,相乘后加入答案
问答案最大可能是多少
限制
Time limit per test: 2 seconds
Memory limit per test: 256 megabytes
\(1\leq R,G,B\leq 200\)
\(1\leq r_i,g_i,b_i\leq 2000\)
解
考虑动态规划
贪心可得,每次取出的两个数一定是对应集合内最大的数字
所以先按照数值从大到小排序
令\(dp[i][j][k]\)表示\(\{R\}\)集合中取了(前)\(i\)个数,\(\{G\}\)集合中取了(前)\(j\)个数,\(\{B\}\)集合中取了(前)\(k\)个数后的最大可能答案
那么我们考虑转移到当前状态的情况
如果当前状态是由取出\(\{R\},\{G\}\)两个集合的元素得到的,那么\(dp[i][j][k]=dp[i-1][j-1][k]+a[i]*b[j]\)
如果当前状态是由取出\(\{R\},\{B\}\)两个集合的元素得到的,那么\(dp[i][j][k]=dp[i-1][j][k-1]+a[i]*c[k]\)
如果当前状态是由取出\(\{G\},\{B\}\)两个集合的元素得到的,那么\(dp[i][j][k]=dp[i][j-1][k-1]+b[j]*c[k]\)
由于答案要取最值,先忽略边界情况,则得到的状态转移方程为
\[dp[i][j][k]=max
\left \{
\begin{aligned}
dp[i-1][j-1][k]+a[i]*b[j]\\
dp[i-1][j][k-1]+a[i]*c[k]\\
dp[i][j-1][k-1]+b[j]*c[k]
\end{aligned}
\right
\}
\]
最后处理下边界情况即可
注意每次转移就尝试更新答案
完整程序
(93ms/2000ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[222],b[222],c[222];
ll dp[222][222][222];
void solve()
{
int R,G,B;
cin>>R>>G>>B;
for(int i=1;i<=R;i++)
cin>>a[i];
for(int i=1;i<=G;i++)
cin>>b[i];
for(int i=1;i<=B;i++)
cin>>c[i];
sort(a+1,a+1+R,greater<int>());
sort(b+1,b+1+G,greater<int>());
sort(c+1,c+1+B,greater<int>());
memset(dp,-0x3f,sizeof dp);
dp[0][0][0]=0;
ll ans=0;
for(int i=0;i<=R;i++) //注意从0开始枚举
for(int j=0;j<=G;j++)
for(int k=0;k<=B;k++)
{
if(i&&j&&k) //四种可转移的情况
dp[i][j][k]=max(dp[i-1][j-1][k]+a[i]*b[j],max(dp[i-1][j][k-1]+a[i]*c[k],dp[i][j-1][k-1]+b[j]*c[k]));
else if(i&&j)
dp[i][j][k]=dp[i-1][j-1][k]+a[i]*b[j];
else if(i&&k)
dp[i][j][k]=dp[i-1][j][k-1]+a[i]*c[k];
else if(j&&k)
dp[i][j][k]=dp[i][j-1][k-1]+b[j]*c[k];
ans=max(ans,dp[i][j][k]);
}
cout<<ans<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
solve();
return 0;
}