01分数规划的理解以及练习poj2976 poj 3621 poj 2728

3 篇文章 0 订阅



              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;
}

这类问题的种类很少  基本属于模板题范畴 不如动态规划这些博大精深···


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值