这几天就水水博客把以前学过的东西整理一下,其实感觉还是比较有必要的,很多东西学了之后很快就忘了QAQ不知是不是我太蠢
https://vjudge.net/problem/POJ-2976
题意:给定A数组B数组,从中选择N-K个使得R最大,输出Round(100*R);
01分数规划问题就是给定两个数组A和B,让你找出m个i使得∑A[i]/∑B[i]最大
我们设∑A[i]/∑B[i]=L,F(L)=∑A[i]-L*∑B[i]
若F[L]大于0,则存在更大的L,否则L只能减小,所以我们二分这个L
【法一】 直接二分L,算出每个i的A[i]-L*B[i],排序后取最大的m个,若和大于0则l=mid,否则r=mid;
//Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<queue> #include<vector> const int maxn=1005; int ans,n,k; double sum,mid,l=1e9,r=-1e9,eps=1e-7; struct node{ double a,b,d; friend bool operator <(const node&A,const node&B){ return A.d<B.d; } }N[maxn]; using namespace std; int check(double L){ sum=0; for(int i=1;i<=n;i++) N[i].d=N[i].a-L*N[i].b; sort(N+1,N+n+1); for(int i=k+1;i<=n;i++) sum+=N[i].d; return sum>0; } int main() { for(;;){ scanf("%d%d",&n,&k); if(!n&&!k) break; for(int i=1;i<=n;i++) scanf("%lf",&N[i].a); for(int i=1;i<=n;i++) scanf("%lf",&N[i].b); for(int i=1;i<=n;i++){ l=min(l,N[i].a/N[i].b); r=max(r,N[i].a/N[i].b); } while(r-l>eps){ mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } ans=floor(l*100+0.5); printf("%d\n",ans); } return 0; }
【法二】Dinkelbach
一种神奇的算法,感觉学一次忘一次,今天又粗略的看了一下,就随便口胡一下。
我们先随便确定一个L,然后求出一个F(L),若它大于0,那么证明F(L)是一个比L更优的解,那么我们直接把解移到F(L)上去。这样一直移动直到达到精度要求。
//Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<queue> #include<vector> const int maxn=1005; int an,n,k; double x,y,ans,L,sum,mid,l=1e9,r=-1e9,esp=1e-7; struct node{ double a,b,d; friend bool operator <(const node&A,const node&B){ return A.d<B.d; } }N[maxn]; using namespace std; void work(){ ans=0; for(;;){ x=0;y=0; for(int i=1;i<=n;i++) N[i].d=N[i].a-ans*N[i].b; sort(N+1,N+1+n); for(int i=k+1;i<=n;i++) x+=N[i].a,y+=N[i].b; L=x/y; if(fabs(L-ans)<=esp) return; ans=L; } } int main() { for(;;){ scanf("%d%d",&n,&k); if(!n&&!k) break; for(int i=1;i<=n;i++) scanf("%lf",&N[i].a); for(int i=1;i<=n;i++) scanf("%lf",&N[i].b); work(); an=floor(ans*100+0.5); printf("%d\n",an); } return 0; }