传送门
题意:一共有n*k张卡牌,标有数字,n个人均分这些卡牌,每个人有自己的幸运数字f[i]
,当某人分得x张幸运数字卡牌,会获得h[x]
点愉悦值,问所有人最大的愉悦值之和是多少。h[0]=0,保证h[i]<h[i+1]
(1<-n<=500,1<=k<=10)
分析:
- 如果每个人的幸运数字都是不同的,那么先把幸运卡牌分给对应的人,然后多余卡牌随便分即可。
- 但是可能有很多人有相同的幸运数字,所以我们需要考虑这样一个问题: num张卡牌 ,分给x个人,如何获得最大愉悦感之和? 很显然是个01背包问题。
- 我们枚举每种幸运数字,
dp[i][j]
表示把j张 分给i个人的最优解 优化掉第一维
dp[j]=max(dp[j],dp[j-t]+h[t]) 枚举t
- 时间复杂度 O(n² * k²)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 1e5+10;
const int mx = 40;
const int mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
//给定n*k个卡牌 均分给n个人 每个人都有幸运数字 拿到x个幸运数字 获得h[x]点幸福值 h 递增 h[0]=0
//幸福值之和的最大值
// 我们单独考虑每种幸运数字
//对于幸运数字L num[L]个L 分配给 lover[l] 个人 最优解
//dp[i][j]=max(dp[i][j],dp[i-1][j-k]+h[k]) 枚举k
//dp[0][0]=0 dp[1][0]=0
int num[maxn];
int h[15];
int f[505];
int n,k;
int lover[maxn];//喜欢数字i的人数
int dp[maxn];
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n*k;i++)
{
int x;scanf("%d",&x);
num[x]++;
}
for(int i=1;i<=n;i++)
{
scanf("%d",f+i);
lover[f[i]]++;
}
for(int i=1;i<=k;i++) scanf("%d",h+i);
ll ans=0;
for(int x=1;x<=n;x++) //枚举每种幸运数字
{
int luck=f[x];
if(!num[luck]) continue;//不存在卡牌 或者 已经分完了
memset(dp,0,sizeof dp);//初始化
for(int i=1;i<=lover[luck];i++) //枚举喜欢这种数字的人数
{
for(int j=num[luck];j>=1;j--) //倒序枚举卡牌个数
{
for(int t=0;t<=min(j,k);t++) //枚举k 第i个人分得t张,转移 wa点记得取min
{
dp[j]=max(dp[j],dp[j-t]+h[t]);
}
}
}
ans+=dp[num[luck]];
num[luck]=0;//分完了
}
cout<<ans;
return 0;
}