01 分数规划的思想的描述如下:令 c=(c1,c2,…,cn) 和 d=(d1,d2,…,dn) 为 n 维整数向量,那么一个 0-1 分数规划问题用公式描述如下 :FP: 最小化或者最大化 (c1x1+…cnxn)/(d1x1…dnxn)=cx/dx xi∈{0,1}。这个问题的解决已经有了成形的算法,但是网上的解释大多过难难以直接理解,大部分对于数学基础的要求是很高的。
先说说这种思想能够解决哪些问题 1:裸的01分数规划pku2976适合刚刚学习的练手以及试模板 2:01分数规划与图形的结合比如说环的判断pku3621 比如最优比例生成树pku2728 解决的思想大致一样 关键是对结论的简要证明,加深理解,然后灵活的运用。
下面谈谈解决01分数规划的方法以及结论
首先,假设要c*x/d*x最大(x,c,d均指向量),假设最优解为 val,设置这样的子问题,z(L)=c*x-d*x*L 分配x向量使 z最大,那么可以知道 如果c*x-L*x*d<0 那么无论怎么分配x向量都无法满足L值即val必然小于L 同样 我们看大于0的情况 这个时候说明可以分配x使得目标解大于L那么val必然大于L,那么很显然 L=val的时候c*x-d*x*L=0,并且z的值具有单调性下面是证明
设L1,L2,且L1>L2
z(L1) = max { c*x-L1*d*x } = c*x1-L1*d*x1(x1是使得其最大的向量) <c*x1-L2*d*x1≤ max{ c*x-d*x*L2 } = z(L2)
=> z(l)是关于L的单调递减函数。
因此我们可以用二分或者迭代来解决
这里只是简单的证明并没有很严格的证明 但是对于使用来讲已经足够。
下面具体使用结论来解决上面的三道例子;
pku2976 很直接的一道题 题意就是简单01分数规划
来说说做法 由于题目已经告诉我们去掉K个x,我们要使得分数最高,设置子问题 z,根据上面的公式很容易写出程序。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<sstream>
#include<string>
#include<climits>
#include<stack>
#include<set>
#include<bitset>
#include<cmath>
#include<deque>
#include<map>
#include<queue>
#define iinf 2000000000
#define linf 1000000000000000000LL
#define dinf 1e200
#define eps 1e-6
#define all(v) (v).begin(),(v).end()
#define sz(x) x.size()
#define pb push_back
#define mp make_pair
#define lng long long
#define sqr(a) ((a)*(a))
#define pii pair<int,int>
#define pll pair<lng,lng>
#define pss pair<string,string>
#define pdd pair<double,double>
#define X first
#define Y second
#define pi 3.14159265359
#define ff(i,xi,n) for(int i=xi;i<=(int)(n);++i)
#define ffd(i,xi,n) for(int i=xi;i>=(int)(n);--i)
#define ffl(i,r) for(int i=head[r];i!=-1;i=edge[i].next)
#define cc(i,j) memset(i,j,sizeof(i))
#define two(x) ((lng)1<<(x))
#define N 555555
#define M 1000000
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
#define Mod n
#define Pmod(x) (x%Mod+Mod)%Mod
using namespace std;
typedef vector<int> vi;
typedef vector<string> vs;
typedef unsigned int uint;
typedef unsigned lng ulng;
template<class T> inline void checkmax(T &x,T y)
{
if(x<y) x=y;
}
template<class T> inline void checkmin(T &x,T y)
{
if(x>y) x=y;
}
template<class T> inline T Min(T x,T y)
{
return (x>y?y:x);
}
template<class T> inline T Max(T x,T y)
{
return (x<y?y:x);
}
template<class T> T gcd(T a,T b)
{
return (a%b)==0?b:gcd(b,a%b);
}
template<class T> T lcm(T a,T b)
{
return a*b/gcd(a,b);
}
template<class T> T Abs(T a)
{
return a>0?a:(-a);
}
using namespace std;
struct node
{
int x,y;
} a[1005];
double low,high,mid;
int cmp(node bb,node cc)
{
return mid*bb.y-bb.x<mid*cc.y-cc.x;
}
int main()
{
int n,k;
double sub,par,res,ans;
while(scanf("%d%d",&n,&k)!=EOF)
{
if(n==0&&k==0) break;
int i,j;
for(i=0; i<n; i++) scanf("%d",&a[i].x);
for(i=0; i<n; i++) scanf("%d",&a[i].y);
low=0;
high=1000;
ans=0;
while(low+eps<high)
{
mid=(low+high)/2.0;
sort(a,a+n,cmp);
sub=par=0;
for(i=0; i<n-k; i++)
{
sub+=a[i].x;
par+=a[i].y;
}
ans=sub/par;
res=mid*par-sub;
if(res<0) low=mid;
else high=mid;
}
printf("%.0lf\n",100*ans);
}
return 0;
}
pku3621 这道题是要求一个最大(边权和/点权和)的环 同样设置子问题 边权-点权*L最大的回路,如果存在并大于0 r=mid 可以直接用spfa的判环 将权编程负值然后判断有没有负环即可
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<sstream>
#include<string>
#include<climits>
#include<stack>
#include<set>
#include<bitset>
#include<cmath>
#include<deque>
#include<map>
#include<queue>
#define iinf 2000000000
#define linf 1000000000000000000LL
#define dinf 1e200
#define eps 1e-3
#define all(v) (v).begin(),(v).end()
#define sz(x) x.size()
#define pb push_back
#define mp make_pair
#define lng long long
#define sqr(a) ((a)*(a))
#define pii pair<int,int>
#define pll pair<lng,lng>
#define pss pair<string,string>
#define pdd pair<double,double>
#define X first
#define Y second
#define pi 3.14159265359
#define ff(i,xi,n) for(int i=xi;i<=(int)(n);++i)
#define ffd(i,xi,n) for(int i=xi;i>=(int)(n);--i)
#define ffl(i,r) for(int i=head[r];i!=-1;i=edge[i].next)
#define cc(i,j) memset(i,j,sizeof(i))
#define two(x) ((lng)1<<(x))
#define N 1050
#define M 10000
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
#define Mod n
#define Pmod(x) (x%Mod+Mod)%Mod
using namespace std;
typedef vector<int> vi;
typedef vector<string> vs;
typedef unsigned int uint;
typedef unsigned lng ulng;
template<class T> inline void checkmax(T &x,T y){if(x<y) x=y;}
template<class T> inline void checkmin(T &x,T y){if(x>y) x=y;}
template<class T> inline T Min(T x,T y){return (x>y?y:x);}
template<class T> inline T Max(T x,T y){return (x<y?y:x);}
template<class T> T gcd(T a,T b){return (a%b)==0?b:gcd(b,a%b);}
template<class T> T lcm(T a,T b){return a*b/gcd(a,b);}
template<class T> T Abs(T a){return a>0?a:(-a);}
struct pp
{
int v,next,w;
}edge[M];
double dist[N];
int f[N],head[N];
bool inque[N];
int cnt[N];
int n,m,tot;
double l,r,ans,mid;
inline void addedge(int u,int v,int w)
{
edge[tot].v=v,edge[tot].w=w,edge[tot].next=head[u],head[u]=tot++;
}
bool spfa()
{
queue<int> q;
ff(i,1,n) dist[i]=iinf;
cc(cnt,0);
cc(inque,0);
q.push(1);
inque[1]=1,cnt[1]=1;
dist[1]=0;
while(!q.empty())
{
int u=q.front();
inque[u]=0;
q.pop();
ffl(i,u)
{
double w=edge[i].w;
int v=edge[i].v;
w=mid*w-f[v];
if(dist[v]>dist[u]+w)
{
dist[v]=dist[u]+w;
if(!inque[v])
{
inque[v]=1;
q.push(v);
cnt[v]++;
if(cnt[v]>=n) return 1;
}
}
}
}
return 0;
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
ff(i,1,n) scanf("%d",f+i);
tot=0;
cc(head,-1);
ff(i,1,m)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
l=0,r=1996;
while(r-l>=0.001)
{
mid=(l+r)/2;
if(spfa())
{
l=mid;
ans=mid;
}
else
r=mid;
}
printf("%.2f\n",ans);
}
return 0;
}
pku2728
最优比例生成树
说白了就是求最小的(代价权和/边权和)的生成树问题 同样设置子问题 代价权-边权*L最小 即重新构造图 然后求最小生成树 同样如果 小于0 自己推导下就会知道 r=mid 另外poj会卡stl 裸奔的prim算法能过 优先队列优化的反而超时
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<sstream>
#include<string>
#include<climits>
#include<stack>
#include<set>
#include<bitset>
#include<cmath>
#include<deque>
#include<map>
#include<queue>
#define iinf 2000000000
#define linf 1000000000000000000LL
#define dinf 1e200
#define eps 1e-5
#define all(v) (v).begin(),(v).end()
#define sz(x) x.size()
#define pb push_back
#define mp make_pair
#define lng long long
#define sqr(a) ((a)*(a))
#define pii pair<int,int>
#define pll pair<lng,lng>
#define pss pair<string,string>
#define pdd pair<double,double>
#define X first
#define Y second
#define pi 3.14159265359
#define ff(i,xi,n) for(int i=xi;i<=(int)(n);++i)
#define ffd(i,xi,n) for(int i=xi;i>=(int)(n);--i)
#define ffl(i,r) for(int i=head[r];i!=-1;i=edge[i].next)
#define cc(i,j) memset(i,j,sizeof(i))
#define two(x) ((lng)1<<(x))
#define N 1050
#define M 1000000
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
#define Mod n
#define Pmod(x) (x%Mod+Mod)%Mod
using namespace std;
typedef vector<int> vi;
typedef vector<string> vs;
typedef unsigned int uint;
typedef unsigned lng ulng;
template<class T> inline void checkmax(T &x,T y){if(x<y) x=y;}
template<class T> inline void checkmin(T &x,T y){if(x>y) x=y;}
template<class T> inline T Min(T x,T y){return (x>y?y:x);}
template<class T> inline T Max(T x,T y){return (x<y?y:x);}
template<class T> T gcd(T a,T b){return (a%b)==0?b:gcd(b,a%b);}
template<class T> T lcm(T a,T b){return a*b/gcd(a,b);}
template<class T> T Abs(T a){return a>0?a:(-a);}
int n,h[N];
struct ps
{
int x,y;
}cor[N];
inline double Distance(ps &p, ps &q )
{
return sqrt((double)sqr(p.x-q.x)+(double)sqr(p.y-q.y));
}
double l,r,ans,mid,dist[N];
double cost[N][N],len[N][N],ww[N][N];
inline void prim()
{
ans=0;
bool in[N]={};
dist[1]=0;
ff(i,2,n) dist[i]=iinf;
ff(i,1,n)
{
double w=iinf;
int id;
ff(j,1,n)
if(!in[j]&&dist[j]<w)
w=dist[j],id=j;
in[id]=1;
ans+=w;
ff(j,1,n)
if(!in[j]&&ww[id][j]<dist[j]) dist[j]=ww[id][j];
}
}
int main()
{
while(scanf("%d",&n)==1&&n)
{
ff(i,1,n)
scanf("%d%d%d",&cor[i].x,&cor[i].y,&h[i]);
ff(i,1,n)
ff(j,i+1,n)
cost[i][j]=cost[j][i]=Abs(h[i]-h[j]),len[i][j]=len[j][i]=Distance(cor[i],cor[j]);
l=0,r=500000;
while(r-l>=eps)
{
mid=(l+r)/2;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
ww[i][j]=ww[j][i]=cost[i][j]-len[i][j]*mid;
prim();
if(ans<0) r=mid;
else
l=mid;
}
printf("%.3f\n",mid);
}
return 0;
}
这类问题的种类很少 基本属于模板题范畴 不如动态规划这些博大精深···
