【XSY2718】gift 分数规划 网络流

题目描述

  有 n 个物品,买第i个物品要花费 ai 元。还有 m 对关系:同时买pi,qi两个物品会获得 bi 点收益。

  设收益为 B ,花费为A,求 BA1

   n400,m200000,1ai,bi100

题解

  显然这是一个分数规划问题。

  二分答案 s ,判断答案是否能大于s

BABBAs>s>As>0

  问题转化成买一个物品获得 ais 点收益,求收益是否能 >0

  显然这个可以用最大权闭合子图来做,点数为 n+m+2 ,边数为 n+3m ,肯定会TLE

  考虑另一种连边方法。

  对于每个点 i ,连边(S,i,ais)

  对于每一对关系,列一个方程,解得:连边 (pi,qi,12bi),(qi,pi,12bi),(pi,T,12bi),(qi,T,12bi)

  答案就是 mi=1bimaxflow

  因为容量为分数不好处理,所以可以把全部数 ×2

  这样做的点数是 n+2 ,边数是 2n+m

  写ISAP不用卡常美滋滋。

代码

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
int rd()
{
    int s=0,c;
    while((c=getchar())<'0'||c>'9');
    s=c-'0';
    while((c=getchar())>='0'&&c<='9')
        s=s*10+c-'0';
    return s;
}
int sum=0;
namespace flow
{
    int c[10000010];
    int v[10000010];
    int t[10000010];
    int h[4010];
    int n;
    void add(int x,int y,int a)
    {
        n++;
        v[n]=y;
        c[n]=a;
        t[n]=h[x];
        h[x]=n;
    }
    void init()
    {
        memset(h,0,sizeof h);
        n=0;
    }
    int S,T;
    int num;
    int d[4010];
    int e[4010];
    int cur[4010];
    int op(int x)
    {
        return ((x-1)^1)+1;
    }
    queue<int> q;
    void bfs()
    {
        memset(d,-1,sizeof d);
        memset(e,0,sizeof e);
        d[T]=0;
        q.push(T);
        int x,i;
        while(!q.empty())
        {
            x=q.front();
            q.pop();
            e[d[x]]++;
            for(i=h[x];i;i=t[i])
                if(c[op(i)]&&d[v[i]]==-1)
                {
                    d[v[i]]=d[x]+1;
                    q.push(v[i]);
                }
        }
    }
    int dfs(int x,int flow)
    {
        if(x==T)
            return flow;
        int s=0,u;
        for(int &i=cur[x];i;i=t[i])
            if(d[v[i]]==d[x]-1&&c[i])
            {
                u=dfs(v[i],min(flow,c[i]));
                s+=u;
                flow-=u;
                c[i]-=u;
                c[op(i)]+=u;
                if(!flow)
                    return s;
            }
        e[d[x]]--;
        if(!e[d[x]])
            d[S]=num;
        e[++d[x]]++;
        cur[x]=h[x];
        return s;
    }
    int solve()
    {
        bfs();
        memcpy(cur,h,sizeof h);
        int ans=0;
        while(ans<2*sum&&d[S]>=0&&d[S]<=num-1)
            ans+=dfs(S,2*sum-ans);
        return ans;
    }
}
using flow::S;
using flow::T;
using flow::num;
int n,m;
int lx[2000010];
int ly[2000010];
int lz[2000010];
int s[4010];
int a[4010];
void add(int x,int y,int z)
{
    flow::add(x,y,z);
    flow::add(y,x,0);
}
void add2(int x,int y,int z)
{
    flow::add(x,y,z);
    flow::add(y,x,z);
}
int check(int x)
{
    flow::init();
    int i;
    S=n+1;
    num=T=n+2;
    for(i=1;i<=n;i++)
    {
        add(S,i,int(min(0x7fffffffll,2ll*a[i]*x)));
        add(i,T,s[i]);
    }
    for(i=1;i<=m;i++)
        if(lx[i]!=ly[i])
            add2(lx[i],ly[i],lz[i]);
    return flow::solve()<2*sum;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
#endif
//  scanf("%d%d",&n,&m);
    n=rd();
    m=rd();
    int i;
    for(i=1;i<=n;i++)
//      scanf("%d",&a[i]);
        a[i]=rd();
    for(i=1;i<=m;i++)
    {
//      scanf("%d%d%d",&lx[i],&ly[i],&lz[i]);
        lx[i]=rd();
        ly[i]=rd();
        lz[i]=rd();
        s[lx[i]]+=lz[i];
        s[ly[i]]+=lz[i];
        sum+=lz[i];
    }
    int l=0,r=sum;
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(check(mid))
            l=mid;
        else
            r=mid-1;
    }
    printf("%d\n",l);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值