A07 01分数规划

 视频链接:A07 01分数规划_哔哩哔哩_bilibili

 

1. POJ2976 Dropping tests

//分数规划+二分+排序 复杂度:nlogn*log(1e4)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=1010;
int n,k;
double a[N], b[N], c[N];

bool check(double x){
  double s=0;
  for(int i=1;i<=n;++i)c[i]=a[i]-x*b[i];
  sort(c+1, c+n+1);
  for(int i=k+1;i<=n;++i) s+=c[i];
  return s>=0;
}
double find(){
  double l=0, r=1;
  while(r-l>1e-4){
    double mid=(l+r)/2;
    if(check(mid)) l=mid;//最大化
    else r=mid;
  }
  return l;
}
int main(){
  while(scanf("%d%d",&n,&k),n){
    for(int i=1;i<=n;i++)scanf("%lf",&a[i]);
    for(int i=1;i<=n;i++)scanf("%lf",&b[i]);
    printf("%.0lf\n", 100*find());    
  }
  return 0;
}

2. Luogu P4377 [USACO18OPEN] Talent Show G

//分数规划+二分+01背包 复杂度:nw*log(1e8)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
int n,W, w[255],t[255];
double f[1005];

bool check(double x){
  for(int i=1;i<=W;i++) f[i]=-1e9;
  for(int i=1;i<=n;i++)
    for(int j=W;j>=0;j--){
      int k=min(W,j+w[i]);
      f[k]=max(f[k],f[j]+t[i]-x*w[i]);
    }
  return f[W]>=0;
}
double find(){
  double l=0,r=1000;
  while(r-l>1e-5){
    double mid=(l+r)/2;
    if(check(mid)) l=mid;//最大化
    else r=mid;
  }
  return r;
}
int main(){
  scanf("%d%d",&n,&W);
  for(int i=1;i<=n;i++)
    scanf("%d%d",&w[i],&t[i]);
  printf("%d\n",int(find()*1000));
  return 0;
}

3. POJ2728 Desert King

//分数规划+二分+最小生成树  复杂度:n*n*log(1e13)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1010
#define inf 123456789.
using namespace std;

int n;
int x[N],y[N],z[N],vis[N];
double a[N][N],b[N][N],d[N];

int geta(int i,int j){return abs(z[i]-z[j]);}
double getb(int i,int j){
  return sqrt(1.*(x[i]-x[j])*(x[i]-x[j])
          +1.*(y[i]-y[j])*(y[i]-y[j]));
}
bool prim(double x){
  memset(vis,0,sizeof vis);
  for(int i=0;i<=n;i++) d[i]=inf;
  d[1]=0; double sum=0;
  for(int i=1;i<=n;i++){
    int t=0; 
    for(int j=1;j<=n;j++)
      if(!vis[j]&&d[j]<d[t]) t=j;
    sum+=d[t]; vis[t]=1;
    for(int j=1;j<=n;j++)
      if(!vis[j]&&a[t][j]-x*b[t][j]<d[j])
        d[j]=a[t][j]-x*b[t][j];
  }
  return sum<=0;
}
double find(){
  double l=0,r=1e7;
  while(r-l>1e-6){
    double mid=(l+r)/2;
    if(prim(mid)) r=mid;//最小化
    else l=mid;
  }
  return r;
}
int main(){
  while(scanf("%d",&n),n){
    for(int i=1;i<=n;i++)
      scanf("%d%d%d",&x[i],&y[i],&z[i]);
    for(int i=1;i<=n;i++)
      for(int j=i+1;j<=n;j++){
        a[i][j]=a[j][i]=geta(i,j);//高度差        
        b[i][j]=b[j][i]=getb(i,j);//距离
      }
    printf("%.3f\n",find());
  }
  return 0;
}

4. Luogu P3199 [HNOI2009]最小圈

//分数规划+二分+负环 复杂度:nm*log(1e17)
#include <iostream>
#include <cstring>
#include <algorithm>
#define N 3005
#define M 10005
using namespace std;

int n,m;
int h[N],to[M],ne[M],tot;
double w[M],d[N];
bool vis[N];

void add(int a,int b,double c){
  to[++tot]=b;ne[tot]=h[a];
  w[tot]=c;h[a]=tot;
}
bool spfa(int u,double x){ //判负环
  vis[u]=1;
  for(int i=h[u];i;i=ne[i]){
    int v=to[i];
    if(d[v]>d[u]+w[i]-x){
      d[v]=d[u]+w[i]-x;
      if(vis[v]||spfa(v,x))return 1;
    }
  }
  vis[u]=0;
  return 0;
}
bool check(double x){
  memset(d,0x3f,sizeof d); 
  memset(vis,0,sizeof vis);  
  for(int i=1;i<=n;i++)
    if(spfa(i,x)) return 1;    
  return 0;
}
double find(){
  double l=-1e7, r=1e7;
  while(r-l>1e-10){
    double mid=(l+r)/2;
    if(check(mid)) r=mid;//最小化
    else l=mid;
  }
  return r;
}
int main(){
  scanf("%d %d",&n,&m);
  for(int i=1;i<=m;i++){
    int x,y; double z;
    scanf("%d %d %lf",&x,&y,&z);
    add(x,y,z);
  }
  printf("%.8lf\n",find());
  return 0;
}
//分数规划+二分+负环 复杂度:nm*log(1e17)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 3005
#define M 10005
using namespace std;

int n,m;
int h[N],to[M],ne[M],tot;
double w[M],d[N];
int vis[N],cnt[N];

void add(int u,int v,double c){
  to[++tot]=v;ne[tot]=h[u];
  w[tot]=c;h[u]=tot;
}
bool spfa(int s,double x){
  queue<int> q;
  q.push(s); vis[s]=1; d[s]=0; 
  while(q.size()){
    int u=q.front();q.pop();vis[u]=0;
    for(int i=h[u];i;i=ne[i]){
      int v=to[i];
      if(d[v]>d[u]+w[i]-x){
        d[v]=d[u]+w[i]-x;
        if(++cnt[v]>50)return 1;//玄学
        if(!vis[v])q.push(v),vis[v]=1;
      }
    }
  }
  return 0;
}
bool check(double x){
  memset(d,0x3f,sizeof d); 
  memset(vis,0,sizeof vis);
  memset(cnt,0,sizeof cnt);  
  for(int i=1;i<=n;i++) 
    if(spfa(i,x)) return 1;
  return 0;
}
double find(){
  double l=-1e7, r=1e7;
  while(r-l>1e-10){
    double mid=(l+r)/2;
    if(check(mid)) r=mid;//最小化
    else l=mid;
  }
  return r;
}
int main(){
  scanf("%d %d",&n,&m);
  for(int i=1;i<=m;i++){
    int x,y; double z;
    scanf("%d %d %lf",&x,&y,&z);
    add(x,y,z);
  }
  printf("%.8lf\n",find());
  return 0;
}

 

练习题:

Luogu P4322 [JSOI2016]最佳团体

//分数规划+二分+树上DP 复杂度:n*n*log(1e9)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=2510;
struct node{
    int v,ne;
}e[N];
int h[N],tot;
void add(int u,int v){
    e[++tot].v=v;e[tot].ne=h[u];
    h[u]=tot;
}
int n,k;
int s[N],p[N]; //招募费用,战斗值
int siz[N],pos[N],cnt;
double val[N],f[N][N];

void dfs(int u){
  siz[u]=1;
  for(int i=h[u];i;i=e[i].ne){
    int v=e[i].v;
    dfs(v); siz[u]+=siz[v];
  }
  pos[++cnt]=u;
}
bool check(double x){
  for(int i=0;i<=cnt;i++)
    for(int j=1;j<=k;j++)
      f[i][j]=-1e9;
  for(int i=1;i<=n;i++)
    val[i]=(double)p[i]-x*s[i];
  for(int i=1;i<=cnt;i++)
    for(int j=1;j<=k;j++)
      f[i][j]=max(f[i-siz[pos[i]]][j]
            ,f[i-1][j-1]+val[pos[i]]);
  return f[cnt][k]>0;
}
double find(){
  double l=0, r=10000;
  while(r-l>1e-5){
    double mid=(l+r)/2;
    if(check(mid)) l=mid;
    else r=mid;
  }
  return l;
}
int main(){
  scanf("%d%d",&k,&n); k++; int x;
  for(int i=1;i<=n;i++){
    scanf("%d%d%d",&s[i],&p[i],&x);
    add(x,i);
  } 
  dfs(0);
  printf("%.3lf\n",find());
  return 0;
}

 Luogu P3705 [SDOI2017]新生舞会

//分数规划+二分+费用流  复杂度:n*m*log(1e12)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

int n,s,t;
int a[105][105];
int b[105][105];
int h[210],tot;
struct edge{
  int v,ne,w; double c;
}e[20500];
void add(int a,int b,int w,double c){
  e[++tot].v=b;e[tot].ne=h[a];
  e[tot].w=w;e[tot].c=c;h[a]=tot;
}
double dis[210];
int pre[210],mf[210],vis[210];
queue<int>q;

bool spfa(){ //找增广路
  for(int i=1;i<=t;i++)
    dis[i]=-1e18, mf[i]=1e9;
  q.push(s);
  while(q.size()){
    int u=q.front();q.pop();
    vis[u]=0;
    for(int i=h[u];i;i=e[i].ne){
      if(!e[i].w)continue;
      int v=e[i].v;
      if(dis[v]<dis[u]+e[i].c){
        pre[v]=i;
        dis[v]=dis[u]+e[i].c;
        mf[v]=min(mf[v],e[i].w);
        if(!vis[v]){
          q.push(v); vis[v]=1;
        }
      }
    }
  }
  return mf[t]<1e9;
}
bool check(double x){
  memset(h,0,sizeof(h)); tot=1;
  for(int i=1;i<=n;i++){ //建图
    add(0,i,1,0); add(i,0,0,0);
    for(int j=1;j<=n;j++){
      double c=a[i][j]-x*b[i][j];
      add(i,j+n,1,c);add(j+n,i,0,-c);
    }
    add(i+n,t,1,0); add(t,i+n,0,0);
  }
  double sum=0;
  while(spfa()){ //找到增广路
    sum+=mf[t]*dis[t];
    for(int x=t;x!=s;){
      e[pre[x]].w--;
      e[pre[x]^1].w++;
      x=e[pre[x]^1].v;
    }
  }
  return sum>=0;
}
double find(){
  double l=0, r=10000;
  while(r-l>1e-8){
    double mid=(l+r)/2;
    if(check(mid)) l=mid;
    else r=mid;
  }
  return l;
}
int main(){
  scanf("%d",&n);
  s=0;t=2*n+1;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)scanf("%d",&b[i][j]);
  printf("%.6f",find());
  return 0;
}

Luogu P2494 [SDOI2011]保密

Luogu P3288 [SCOI2014]方伯伯运椰子

Luogu P6087 [JSOI2015]送礼物

UVA1389 Hard Life

 

posted @ 2023-02-08 10:33  董晓  阅读(881)  评论(1编辑  收藏  举报