01分数规划与最优比率环,最优比率生成树
1.luogu 4322 最佳团体,01分数规划+树形dp
#include<bits/stdc++.h> #define rep(i,x,y) for(register int i=x;i<=y;i++) #define fill(x,t) memset(x,t,sizeof(x)) const double EPS=1e-4; const int N=2505; const int E=5005; using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f;} double f[N][N],tmp[N],v[N]; int siz[N],a[N],b[N],head[N],tot; struct node{int u,v,next;}e[E]; void insert(int u,int v) { e[++tot]=(node){u,v,head[u]};head[u]=tot; e[++tot]=(node){v,u,head[v]};head[v]=tot;} void dfs(int u,int fa) { siz[u]=1;f[u][0]=0;f[u][1]=v[u]; for (int i=head[u];i;i=e[i].next){ int v=e[i].v; if(v==fa) continue; dfs(v,u); rep(j,1,siz[u]+siz[v]) tmp[j]=f[0][N-1]; rep(j,1,siz[u])rep(k,0,siz[v]) tmp[j+k]=max(tmp[j+k],f[u][j]+f[v][k]); siz[u]+=siz[v]; rep(j,1,siz[u]) f[u][j]=tmp[j]; } } int main(){ int n,m; scanf("%d%d",&m,&n); rep(i,1,n){ int fa;scanf("%d%d%d",&b[i],&a[i],&fa); insert(fa,i);} double l,r; for (l=0,r=10000;r-l>=EPS;) { double mid=(l+r)*0.5; fill(f,0xc2); rep(i,1,n) v[i]=(double)a[i]-mid*(double)b[i]; dfs(0,0); if(f[0][m+1]>0) l=mid; else r=mid;} printf("%.3lf\n", l);return 0; }
2.最优比率环
luogu 2868 n点m有向边,点上权值为a,边上权值b求 sigma(a[i])/sigma(b[i]) 最大
仿照01分数规划模型
想到二分一个mid判定图上是否存在一个环s
上式子可转化为 sigma (a[i]-mid*b[i])>0 ,a,b分别对应点集和边集
因为实际作上式子十分麻烦
故判定 sigma (mid*b[i]-a[i])<0 判断图中是否存在负环,利用dfs的spfa(快)
bfs版本
#include<bits/stdc++.h> #define rep(i,x,y) for(register int i=x;i<=y;i++) #define dec(i,x,y) for(register int i=x;i>=y;i--) #define ll long long using namespace std; const int N=1010; inline int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f;} double dis[N];int n,m,a[N]; int head[N],tot;int vis[N],num[N]; struct node{int v,w,next;}e[5010]; void insert(int u,int v,int w){ e[++tot]=(node){v,w,head[u]};head[u]=tot;} bool check(double k){ queue<int> q; rep(i,1,n){q.push(i);dis[i]=0;vis[i]=num[i]=1;} while(!q.empty()){ int u=q.front(); q.pop();vis[u]=0; for(int i=head[u];i;i=e[i].next){ int v=e[i].v;double w=e[i].w; if(dis[v]>dis[u]+k*w-(double)a[u]){ dis[v]=dis[u]+k*w-(double)a[u]; if(!vis[v]){ q.push(v);vis[v]=1; if(++num[v]>=n) return 1; } } } } return 0; } int main(){ n=read(),m=read(); rep(i,1,n) a[i]=read(); rep(i,1,m){ int u=read(),v=read(),w=read();insert(u,v,w);} double l=0,r=1000010,mid; while(r-l>1e-6){ double mid=(l+r)/2.0; if(check(mid)) l=mid;else r=mid;} printf("%.2lf\n",l);return 0; }
dfs版
#include<bits/stdc++.h> #define rep(i,x,y) for(register int i=x;i<=y;i++) #define dec(i,x,y) for(register int i=x;i>=y;i--) #define ll long long using namespace std; const int N=51000; inline int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f;} bool vis[N];int n,m,x,y,z,tot; int a[N],num[N],head[N]; double ans,mid,l,r,w[N],dis[N]; struct node{int v,w,next;}e[N]; void insert(int u,int v,int w){ e[++tot]=(node){v,w,head[u]};head[u]=tot;} int spfa(int u){ vis[u]=1; for(int i=head[u];i;i=e[i].next){ int v=e[i].v; if(dis[v]>dis[u]+w[i]){ dis[v]=dis[u]+w[i]; if(vis[v]||spfa(v)){ vis[u]=0; return 1; } } } vis[u]=0; return 0; } bool check(){ rep(i,1,n) if(spfa(i)) return 1;return 0; } int main(){ n=read(),m=read(); rep(i,1,n) a[i]=read(); rep(i,1,m){ int x=read(),y=read(),z=read();insert(x,y,z);} l=0,r=100005; while(r-l>1e-7){ mid=(l+r)/2.0; rep(i,1,tot){ int v=e[i].v; w[i]=(double)mid*e[i].w-a[v]; } if(check()) l=mid;else r=mid; } printf("%.2lf\n",l);return 0; }
3.最优比率生成树
日后填坑