传送门:
http://codeforces.com/problemset/problem/609/D
题意:
给出每天的人民币兑换美元和英镑的比率
有m个物品,每个物品所需的美元和英镑是不变的,一共n天,s人民币,问最小天数可以买到k个,如果不能买到k个的话就输出-1!
注意是每个物品只能用dollar or pound 去买,因为下面给了1,和2指的就是编号!
题解:Two pointers or binary search
先记录一下我想这道题时候的心路历程,以下大部分内容均可以直接忽略掉
首先粗糙的想法就是把dollar 和 pound 买的分开来指,在外指针往右移的时候,内指针肯定是往左移动,哦哦注意求的是最少天数
二分的写法就是先预处理出每种物品买i个需要的最少钱,然后在枚举一种的时候要二分去查找剩余的钱买剩余个的时候最少是第几天
首先要用min天处理前多少天的最小值,
肯定是枚举答案的天数,然后去二分判断是否合法嘛,那么这样显然是不行的
原来是需要这个样子,我就一直在纠结前n-1天能满足,那么前n天就一定满足,这个性质要怎么去使用,就是被之前的lower_bound想法带跑偏了!
正确应该是二分枚举天数(因为这个具有单调性),然后再去O(n)判断合法性,复杂度O(n*log(n))
先写一发试一试哈
本来判断是否合法想的是枚举一种买多少个,另一种买多少个,但这样写着实是比较费劲,应该重新把商品加到对应集合里面去,然后排序后取前k个即可,还是应该想清楚了再去写,否则会绕弯路。最后复杂度是又多了了log(n),不过也没关系啊!!
本题的二分容易写屎,注意套路,本题的使用方法是详见代码吧
最后判断的时候条件是l正好等于r-1即n
然后就可以了,接下来再来一发two pointers的,感觉这样写还是很容易写屎的
暂没有发发现two pointers 的写法,其实按我之前想的那样预处理 也可以啦,速度还要快好多呢, 只需要在最外层数枚举dollar的物品数就ok了,然后二分去找最少需要多少天实现这种方案,最后再更新一下最小的天数!!!
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=200009;
int a[maxn],b[maxn],t[maxn],c[maxn];
vector<pair<long long, pair<int, int> > >items;
int n,m,s,k;
bool solve(int day)
{
items.clear();
int mina=INF,minb=INF;
int posa,posb;
for (int i=1; i<=day; i++) {
if (a[i]<mina) {
mina=a[i];
posa=i;
}
if (b[i]<minb) {
minb=b[i];
posb=i;
}
}
for (int i=1; i<=m; i++) {
if (t[i] == 1) items.push_back(make_pair(1ll*mina*c[i], make_pair(i, posa)));
else items.push_back(make_pair(1ll*minb*c[i], make_pair(i, posb)));
}
long long sum=0;
sort(items.begin(), items.end());
for (int i=0; i<k; i++)
sum += items[i].first;
return s >= sum;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&s);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1;i<=m;i++)scanf("%d%d",&t[i],&c[i]);
int l=0,r=n+1;
while(l+1<r){
int mid=(l+r)/2;
if(solve(mid))r=mid;
else l=mid;
}
if (l == n) {
printf("-1\n");
return 0;
}
printf("%d\n", r);
solve(r);
for (int i=0;i<k; i++) {
printf("%d %d\n", items[i].second.first, items[i].second.second);
}
return 0;
}