分数规划

题目一:洛谷P4377 [USACO18OPEN] Talent Show G

思路:
考虑01背包。
二分一个答案x,把wi作为第i个物品的重量,ti-x*wi作为第i个物品的价值,那么dp[n][W]就是最大值。另外,Σwi可能超过W,此时直接视为W即可。若dp[n][W]>=0,就继续最大化x。同时滚动数组,降低维数。
代码:
cpp
int n,W;
int w[N],t[N];
double dp[N*4]; //dp[i]:t[i]-x*w[i];
bool check(double x){
for(int i=1;i<=W;i++) dp[i]=-1e9;
for(int i=1;i<=n;i++){
for(int j=W;j>=0;j--){
int k=min(W,j+w[i]);
dp[k]=max(dp[k],dp[j]+t[i]-x*w[i]);
}
}
return dp[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;
}
void solve(){
cin>>n>>W;
for(int i=1;i<=n;i++){
cin>>w[i]>>t[i];
}
int ans=find()*1000;
cout<<ans<<endl;
}
题目二:P3199 [HNOI2009] 最小圈

思路:
0/1分数规划
每条边边权wi,求一个环C使环C中Σwi/Σ1最小。
二分一个答案x,把wi-x作为边权,那么最小环就是最小值,判断最小值是否<=0,等价于判负环。
代码:
cpp
struct edge{
int v;
double w;
edge(int a,double b){
v=a;
w=b;
}
};
vector<edge> e[N];
bool vis[N];
double d[N];
int n,m;
bool spfa(int u,double x){
vis[u]=1;
for(auto ei:e[u]){
int v=ei.v;
if(d[v]>1.0*d[u]+ei.w-x){
d[v]=1.0*d[u]+ei.w-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;
}
void solve(){
cin>>n>>m;
int u,v,w;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
e[u].push_back(edge(v,w));
}
printf("%.8lf\n",find());
}