【演算法】01分數規劃
阿新 • • 發佈:2018-11-12
昨天做訓練賽的時候遇到了一道求最優比率的題,不會寫,學長說是用01分數規劃來做,於是就看了一下入門級別的。在這裡先寫一下自己的心得。
01分數規劃就是利用二分來查詢最優比率的問題。
首先我們看一下nyoj的一道題目:Yougth的最大化
題意是每個物品都有自己的價值和重量,讓你選K個物品使得這K個物品的單位價值即(v/w)最大。我們假設單位價值為t;
那麼對於單個物品就有
,即
我們這樣就可以根據這個等式二分查詢
的最大值
我們用一個數組記錄下來每個物品
的值,然後進行排序再二分。二分的條件是
。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define clr(s, x) memset(s, x, sizeof(s))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
inline int read(){int r=0;char c=getchar();while(c<'0'||c>'9') {c=getchar();}while(c>='0'&&c<='9') {r=r*10+c-'0';c=getchar();}return r;}
inline ll readll(){ll r=0;char c=getchar();while(c<'0'||c>'9') {c=getchar();}while(c>='0'&&c<='9') {r=r*10+c-'0';c=getchar();}return r;}
inline ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&1)res = (res*a)%mod;a=(a*a)%mod;b>>=1;}return res;}
inline ll gcd(ll a,ll b){while(b^=a^=b^=a%=b);return a;}
const double eps = 1e-10;
const ll LLINF = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const int MAXN = 1e6;
const int MAXM = 1e5;
//v/w=單價->v-w*單價==0
struct node{
double w,v;
}a[MAXN];
double re[MAXN];
int n,k;
bool jud(double x){
for(int i=1;i<=n;i++){
re[i]=a[i].v-a[i].w*x;
}
sort(re+1,re+n+1);
double sum=0;
for(int i=n;i>n-k;i--){
sum+=re[i];
}
return sum>=0?1:0;
}
int main(){
while(cin>>n>>k){
double mxx=-INF;
for(int i=1;i<=n;i++){
cin>>a[i].w>>a[i].v;
mxx=max(mxx,a[i].v/a[i].w);
}
double l=0;
double r=mxx;
double mid;
while(r-l>eps){
mid=(l+r)/2;
if(jud(mid)){
l=mid;
}
else{
r=mid;
}
}
printf("%.2lf\n",l);
}
return 0;
}
另一道poj類似的題目Dropping tests
讓你刪除k個物品使得比率最大,其實就是拿n-k個物品。
#include<iostream>
#include<algorithm>
using namespace std;
const double eps = 1e-10;
typedef long long ll;
const int MAXN = 1e5;
const int MAXM = 1e5;
//a/b==t a-b*t==0//二分t即可
double a[MAXN];
double b[MAXN];
double re[MAXN];
int n,k;
bool cmp(double a,double b){
return a>b;
}
bool jud(double x){
for(int i=1;i<=n;i++){
re[i]=a[i]-b[i]*x;
}
sort(re+1,re+n+1,cmp);
double sum=0;
for(int i=1;i<=n-k;i++){
sum+=re[i];
}
return sum>=0?1:0;
}
int main(){
while(cin>>n>>k&&(n+k)){
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
double mxx=0;
for(int i=1;i<=n;i++) mxx=max(mxx,a[i]/b[i]);
double l=0;
double r=mxx;
double mid;
while(r-l>eps){
mid=(l+r)/2;
if(jud(mid)){
l=mid;
}
else r=mid;
}
cout<<(ll)((l)*100.0+0.5)<<endl;
}
return 0;
}