A07 01分数规划
//分数规划+二分+排序 复杂度: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; }
//分数规划+二分+最小生成树 复杂度: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; }
//分数规划+二分+负环 复杂度: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; }
练习题:
//分数规划+二分+树上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; }
//分数规划+二分+费用流 复杂度: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; }