小咪买东西(二分+01分数规划)
小咪买东西
小咪是一个土豪手办狂魔,这次他去了一家店,发现了好多好多(n个)手办,但他是一个很怪的人,每次只想买k个手办,而且他要让他花的每一分钱都物超所值,即:买下来的东西的总价值/总花费=max。请你来看看,他会买哪些东西吧。
输入描述:
多组数据。
第一行一个整数T,为数据组数。
接下来有T组数据。
对于每组数据,第一行两个正整数n,k,如题。
接下来n行,每行有两个正整数ci,vi。分别为手办的花费和它对于小咪的价值。
输出描述:
对于每组数据,输出一个数,即能得到的总价值/总花费的最大值。精确至整数。
示例1
输入
1 5 1 1 2 2 3 3 4 4 5 5 6
输出
2
分析:这道题要求单位价值(题目中的总价值/总花费)最大,假设单位价值为x,那么可以知道,x = Σv / Σc。
将公式变形得到 Σv - Σc * x = 0 我们要求的是x的最大值,只要我们保证在式子Σv - Σc * x > 0的前提下,使x尽量大就可以。
x可以枚举,但是限于题目数据,需要使用二分进行查找枚举。x的初始范围只需要设定在题目所给范围内(1~1e4)即可。
可以用数组w来存储每个v和c通过公式v-c*x得到的值,然后对w[]再进行从大到小的排序,前k个就是所需要的。
前k个的和sum如果大于等于0,说明x还可以再大,如果小于零,需要让变小些。这就是二分的选择条件。
代码如下:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn=1e4+10; const int inf=0x3f3f3f3f; int n,k; int c[maxn],v[maxn]; double w[maxn]; bool judge(double x) { for(int i=1;i<=n;i++) { w[i]=v[i]-x*c[i]; } sort(w+1,w+1+n,greater<double>()); double sum=0; for(int i=1;i<=k;i++) { sum+=w[i]; } return sum>=0; } int main() { int t; cin>>t; while(t--) { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d%d",&c[i],&v[i]); } int l=1,r=maxn; while(l<=r) { double mid=(l+r)/2; if(judge(mid)) l=mid+1; else r=mid-1; } printf("%d\n",(int)r); } return 0; }
公式的变形并对其进行分析是二分中一个重要的点,找到关键未知数,进行二分查找,判断是否符合条件。
题目 解方程 也是通过对公式的观察分析得出
可以仅通过枚举a,b的值,用公式ax2+bx+c=0变形-c=a*x*x+b*x 来二分查找-c是否在所给数中判断是否符合题意。
代码如下:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn=1010; int a[maxn]; int n,x; bool findc(int num) { int l=1,r=n; while(l<=r) { int mid=(l+r)/2; if(a[mid]<num) l=mid+1; else if(a[mid]>num) r=mid-1; else return true; } return false; } int main() { cin>>n>>x; for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1); bool flag=false; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { int c=a[i]*x*x+a[i]*x; if(findc(-c)) { flag=true; break; } } } if(flag) printf("YES"); else printf("NO"); return 0; }