Reading Books (easy version)
原题链接:传送门
题目大意
你有n本书,每本书有三个附带信息 t , a , b.
t: 读完这本书所花费的时间
a: 表示Alice是否喜欢这本书
b: 表示Bob是否喜欢这本书
0 表示不喜欢 1 表示喜欢。现在给你这 n 本书的信息,要求Alice和Bob没人必须读k本书,求读完这本书的最小时间代价。如果不存在答案则输出-1
分析
这个题意得话,一眼望过去就知道是贪心 + 排序,于是我一开始对所有得书本进行排序让公共喜欢得书籍放在前面,其余得按照时间排序,于是WA on test 5.
于是开始转换思路,因为上面得方法存在漏洞,就是如果我当前选择依次读完两本书得话还不如两个人各自读完一本书所消耗得代价小的话那么我还不如让其分开去读。
这样确立贪心策略:两个人同时选择自己喜欢的书,且选择当前消耗代价总和最小的那一种情况。
这题的思路其实很好想,关键在于你要如何实现这个方法。这也是对这道题写题解的原因。提升一下自己的代码能力,对于边界情况和情况分类要卡好否则就会WA掉
AC 代码
自己AC代码
void slove()
{
int n, k; cin >> n >> k;
vector<int> com, Ali, Bob;
for (int i = 0; i < n; i++)
{
int t, a, b;
cin >> t >> a >> b;
if ((a + b) == 2)com.PB(t);
if (a == 1 && b == 0)Ali.PB(t);
if (b == 1 && a == 0)Bob.PB(t);
}
sort(com.begin(), com.end(),greater<int>());
sort(Ali.begin(), Ali.end(),greater<int>());
sort(Bob.begin(), Bob.end(),greater<int>());
int ic = com.size() - 1 , ia = Ali.size() - 1 , ib = Bob.size() - 1;
int ans = 0,A = k , B = k;
while(ic >= 0 || (ia >= 0 && ib >= 0))
{
if(A == 0 && B == 0)break;
if((ia < 0 || ib < 0) && ic >= 0) // 如果两个之中有一个没有了
{
ans += com[ic];ic--;A--,B--;
continue;
}
if((ic >= 0) && com[ic] <= Bob[ib] + Ali[ia])
{
ans += com[ic];ic--;A--,B--;
}else {
if(ib >= 0)ans += Bob[ib],ib--,B--;
if(ia >= 0)ans += Ali[ia],ia--,A--;
}
}
if(A > 0 || B > 0)cout << -1 << endl;
else cout << ans << endl;
}
标程/优秀代码
题解代码中巧妙地运用了前缀和地思想极大地简化了代码。
void slove()
{
int n , k;
cin >> n >> k;
vector<int> times[4];
vector<int> sums[4];
for (int i = 0; i < n; ++i)
{
int t, a, b;
cin >> t >> a >> b;
times[a * 2 + b].push_back(t);
}
// 对每一个序列进行排序并获取其前缀和
for (int i = 0; i < 4; ++i)
{
sort(times[i].begin(), times[i].end());
sums[i].push_back(0);
for (auto it : times[i])
{
sums[i].push_back(sums[i].back() + it);
}
}
int ans = MAX;
// 枚举去了多少次公共的部分 如果不存在
for(int i = 0;i < min(k + 1,(int)sums[3].size());i ++)
{
// 取完公共的部分取剩下的部分即 k - i 但是要确保k - i在每个序列范围内才进行更新 ans
if(k - i < sums[1].size() && k - i < sums[2].size())
{
ans = min(ans ,sums[3][i] + sums[1][k-i] + sums[2][k-i]);
}
}
if(ans == MAX)ans = -1;
cout << ans << endl;
}