补牙【题解】

题目描述

来自火星的女孩Katy牙疼了呜呜。

火星人的身体构造是非常复杂的,他们有 n 颗牙齿,m 个牙龈,每颗牙齿都位于其中一个牙龈上。对于第 i 颗牙齿,Katy如果需要进行治疗的话,就需要支付牙医 Bi 欧元,此外,如果要治第 j 个牙龈上的牙齿,就还需要支付 Aj 元麻醉费用。如果Katy的父母准备了 t 欧元进行治疗,那么Katy能治好牙齿的最大数量是多少?

输入格式

第一行,三个整数 n,m,t(1 ≤ n ≤ 600;1 ≤ m ≤ n;1 ≤ t ≤ 10^6)。
第二行包含 k 个整数 A1,A2,A3,…,Am,表示麻醉第 j 个牙龈时支付的麻醉费用。(1 ≤ bj ≤ 600)
接下来n行,每行两个整数 Bi 和 Ci,Bi表示治疗第 i 颗牙齿时需要支付的费用,Ci 表示这个牙齿长在了哪一个牙龈上。(1 ≤ ai ≤ 600;1 ≤ ci ≤ m)

输出格式

第一行为能治疗牙齿的最大数量。
第二行,若干个整数,表示治疗的牙齿的编号(递减输出)。如果有多种解决方案,任意输出其中一种即可。

输入样例

4 2 10
1 2
1 2
5 2
3 1
3 2

输出样例

3
4 3 1

解题思路

由于输入时的牙齿顺序非常乱,我们不妨用 s[i][j] 表示第 i 个牙龈上的第 j 个牙齿的治疗费用,ss[i] 表示第 i 个牙龈上第 j 个牙齿在输入时的编号(方便输出方案)。
我们用贪心的思想考虑,在同一个牙龈上治疗 x 个牙齿,我们必定会选择费用最小费用的 x 个牙齿。


于是,对于每个牙龈,先按 s[i] 对 s[i] 和 ss[i] 排序,然后设 p[i][j] 表示第 i 个牙龈上前 j 个牙齿的治疗总费用。然后把 i 看作组别,p[i][j] 看作选择第 j 个物品的利益,j 看作物品的代价,接下来就是分组背包的工作了。

分组背包

设 f[i][j] 表示考虑到前 i 个牙龈后治疗了 j 个牙齿的最小费用。那么对于治疗了前 i 个牙龈的 j 颗牙齿,有

f[i][j]=min(f[i][j],f[i-1][j-k]+p[i][k]+a[i])

其中 k=0~min(j,s[i]的长度)
初始化 f[i][j]=无穷大,f[0][0]=0。

输出方案

采用递归+贪心的方法,从 f[m][n] 开始,枚举第 n 个牙龈治疗的牙齿数量 k,如果 f[m][n]=f[m-1][n-k]+p[m][k]+a[m],说明找到了 f[m][n] 转移的路径(f[m][n]是从f[m-1][n-k]+p[m][k]+a[m]转移而来),那么即可确定第 n 个牙龈治疗了 k 颗牙齿。接下来的 n-1 颗牙齿也以此类推。

AC代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,t,a[605],x,y,f[605][605];
int s[605][605],ss[605][605],w,p[605][605];
int ans[605];
void print(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值