【分数规划】
分数规划
==建议先看资料照着推一下 知道怎么回事了以后忘了也会推...
这样一类问题,给定两个数组,benifit[i]表示选取i的收益,cost[i]表示选取i的代价。如果选取i,定义x[i]=1否则x[i]=0。每一个物品只有选或者不选两种方案,求一个选择方案使得\(R=\sum(benifit[i]*x[i])/\sum(cost[i]*x[i])\)取得最值,即所有选择物品的总收益与总代价的比值最大或是最小
我的笔记当然是在笔记本上鸭
实战
POJ2976Dropping tests 普通01分数规划
题意:给出n个a和b,让选出n-k个使得\((\sum a[i])/(\sum b[i])\)最大
普通分数规划 就是因为是实数要注意一些小细节
二分
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=1e5+5,M=5e5+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,k,a[N],b[N];
double d[N];
template void rd(t &x){
x=0;int w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=w?-x:x;
}
double check(double l){
double sum=0.0;
for(int i=1;i<=n;++i) d[i]=(double)a[i]-l*b[i];
sort(d+1,d+n+1);
for(int i=n-k+1;i<=n;++i) sum+=d[i];
return sum;
}
int main(){
freopen("bb.txt","r",stdin);
rd(n),rd(k);
double l=0.0,r=0.0,mid;
for(int i=1;i<=n;++i) rd(a[i]);
for(int i=1;i<=n;++i){
rd(b[i]);
r=Max(r,1.0*a[i]/b[i]);
}
while(r-l>=eps){
mid=(l+r)/2;
if(check(mid)>0) l=mid;
else r=mid;
}
printf("%.4lf\n",l);
return 0;
}
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
const int N=1000+5,M=5e5+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,k;
double a[N],b[N],d[N];
template <class t>void rd(t &x){
x=0;int w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=w?-x:x;
}
double check(double l){
double sum=0.0;
for(int i=1;i<=n;++i) d[i]=a[i]-l*b[i];
sort(d+1,d+n+1);
for(int i=k+1;i<=n;++i) sum+=d[i];
return sum;
}
int main(){
while(scanf("%d%d",&n,&k)!=EOF&&n+k){
double l=0.0,r=0.0,mid;
for(int i=1;i<=n;++i) scanf("%lf",&a[i]);
for(int i=1;i<=n;++i){
scanf("%lf",&b[i]);
if(a[i]/b[i]>r) r=a[i]/b[i];
}
while(r-l>eps){
mid=(l+r)/2;
if(check(mid)>0) l=mid;
else r=mid;
}
printf("%.0f\n",l*100);
}
return 0;
}
POJ2728Desert King 最优比率生成树
有n个点,每个点有一个坐标和高度
两点之间的费用是高度之差的绝对值 两点之间的距离就是欧几里得距离
求一棵生成数,使得单位距离的费用最小
即求得花费/收益比值最小
(开始没懂...原来是没好好读题)
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=1000+5,M=5e5+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,k;
double a[N][N],b[N][N];
template <class t>void rd(t &x){
x=0;int w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=w?-x:x;
}
struct node{int x,y,h;}nd[N];
double qdis(int i,int j){return sqrt(1.0*(nd[i].x-nd[j].x)*(nd[i].x-nd[j].x)+1.0*(nd[i].y-nd[j].y)*(nd[i].y-nd[j].y));}
bool vis[N];
double dis[N],d[N][N];
double prim(double x){
double sum=0.0;
for(int i=0;i<=n;++i) dis[i]=1e20,d[i][i]=0.0,vis[i]=0;
dis[1]=0.0;
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
d[i][j]=d[j][i]=b[i][j]-x*a[i][j];
for(int i=1,u=0;i<=n;++i,u=0){
for(int j=1;j<=n;++j)
if(!vis[j]&&dis[j]<dis[u]) u=j;
vis[u]=1,sum+=dis[u];
for(int v=1;v<=n;++v)
if(!vis[v]) dis[v]=Min(dis[v],d[u][v]);
}
return sum;
}
int main(){
freopen("in.txt","r",stdin);
while(scanf("%d",&n)!=EOF&&n){
double l=0.0,r=0.0,mid;
for(int i=1;i<=n;++i) rd(nd[i].x),rd(nd[i].y),rd(nd[i].h);
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
a[i][j]=a[j][i]=qdis(i,j),b[i][j]=b[j][i]=1.0*Abs(nd[i].h-nd[j].h),r=Max(r,b[i][j]/a[i][j]);
while(r-l>=eps){
mid=(l+r)/2;
if(prim(mid)>0) l=mid;
else r=mid;
}
printf("%.3f\n",l);
}
return 0;
}
USACOSightseeing Cows 最优比率环
在有向图图中选出一个环,使得这个环的点权/边权最大
即在每次check的时候找一个正环 可是正环并不好找 就将边权改为负值去找负环
打了一个下午超级悲伤 不知道哪里错了一直给我报错 然后怒而全部删掉重打 然后一遍过... 一言难尽
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=1000+5,M=5000+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,m,a[N],fr[M],to[M],co[M];
template <class t>void rd(t &x){
x=0;int w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=w?-x:x;
}
int head[N],tot;
struct edge{int v,nxt;double w;}e[M];
void add(int u,int v,double w){
e[++tot]=(edge){v,head[u],w},head[u]=tot;
}
bool vis[N];double dis[N];
int cnt[N];
queue<int>q;
bool spfa(){
while(!q.empty()) q.pop();
for(int i=1;i<=n;++i) dis[i]=0.0,cnt[i]=vis[i]=1,q.push(i);//图有可能不连通
while(!q.empty()){
int u=q.front();
q.pop(),vis[u]=0;
for(int i=head[u],v;i;i=e[i].nxt){
v=e[i].v;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
if(!vis[v]) q.push(v),vis[v]=1,++cnt[v];
if(cnt[v]>=n) return 1;
}
}
}
return 0;
}
bool check(double x){
memset(head,0,sizeof(head)),tot=0;
for(int i=1;i<=m;++i) add(fr[i],to[i],-((double)a[to[i]]-x*co[i]));
if(spfa()) return 1;//找到负环
else return 0;
}
int main(){
freopen("in.txt","r",stdin);
rd(n),rd(m);
for(int i=1;i<=n;++i) rd(a[i]);
for(int i=1;i<=m;++i) rd(fr[i]),rd(to[i]),rd(co[i]);
double l=0.0,r=3000.0,mid;
while(r-l>=eps){
mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.2f",l);
return 0;
}
[USACO18OPEN]Talent Show 分数规划+背包
使总重量最少为W的奶牛可能达到的总才艺值与总重量的比值最大
01分数规划 然后跑背包来判断是否可行 初始化值时要赋为极小是lch讲的时候强调过的QAQ
我太瘟辽 该好好练一下动规 在看到重量最少为多少时想了好一会儿怎么转移
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=300+5,M=1000+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,W,a[N],b[N];
template <class t>void rd(t &x){
x=0;int w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=w?-x:x;
}
double d[N],f[N][M];
bool check(double mid){
for(int i=0;i<=n;++i){
if(i) d[i]=(double)a[i]-mid*b[i];
for(int j=0;j<=W;++j) f[i][j]=-INF;
}
f[0][0]=0.0;
for(int i=1;i<=n;++i)
for(int j=0;j<=W;++j)
f[i][j]=Max(f[i][j],f[i-1][j]),
f[i][Min(W,j+b[i])]=Max(f[i][Min(W,j+b[i])],f[i-1][j]+d[i]);
return f[n][W]>0;
}
int main(){
freopen("in.txt","r",stdin);
rd(n),rd(W);
double l=0.0,r=0.0,mid;
for(int i=1;i<=n;++i) rd(b[i]),rd(a[i]),r=Max(r,(double)a[i]/b[i]);
while(r-l>=eps){
mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%d",(int)(l*1000));
return 0;
}
==还有[SCOI2014]方伯伯运椰子 [SDOI2017]新生舞会 都是分数规划+网络流 懒得做了