GDKOI2016 Day1 T3 寻宝

T3 寻宝

给出N个点,点与点之间有依赖关系,形如选了这个点,必须要选哪些点。每个点有两个权,a和b,求选出一些合法的点集,使得 biai 最小化。若无解则输出一个奇怪的字符串。

01分数规划。如果答案是l,则满足 biai=l 移项得 bilai=0 。发现最小值很难看,于是聪明滴把两边同时乘一个-1,得 laibi=0 取最大值,然后有依赖关系就可以用最大权闭合子图做了。
二分一个mid把每个点的点权变成 midab ,然后对原图找一个最大权闭合子图,判断是否大于0即可。这个东西可以用最大流(最小割)来解决,对于每一个依赖关系连一条容量为+∞的边,从S向每一个正权点连容量为其权值的边,每一个负权点向T连一条容量为其权值的相反数的边,然后最终答案就是所有正权点的权值和-最小割。
证明请感性理解。(似乎所有网络流的证明都得感性理解)
注意判无解,感觉我的方法有点水。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define inf 0x7fffffff
#define db double
#define N 1005
#define M 55005
using namespace std;
int n,x,y,S,T,l,ans,dis[N],d[N],a[N][N],b[N],c[N];
int t[M],next[M],last[N];
db f[M];
void add(int x,int y,db z) {
    t[++l]=y;f[l]=z;next[l]=last[x];last[x]=l;
    t[++l]=x;f[l]=0;next[l]=last[y];last[y]=l;
}
bool bfs() {
    memset(dis,0,sizeof(dis));dis[S]=1;d[1]=S;
    int i=0,j=1;
    while (i<j) 
        rep(k,d[++i]) if (f[k]&&!dis[t[k]]) {
            dis[t[k]]=dis[d[i]]+1;d[++j]=t[k];  
        }
    return dis[T];
}
db dinic(int x,db y) {
    if (x==T) return y;
    db now=0;
    rep(i,x) if (f[i]&&dis[t[i]]==dis[x]+1) {
        db k=dinic(t[i],min(y,f[i]));
        f[i]-=k;f[i^1]+=k;now+=k;y-=k;
        if (!y) break;
    }
    if (!now) dis[x]=-1;return now;
}
bool check(db x) {
    l=1;memset(last,0,sizeof(last));
    db ans=0;
    fo(i,1,n)
        if (x*b[i]-c[i]>0) add(S,i,x*b[i]-c[i]),ans+=x*b[i]-c[i];
        else add(i,T,c[i]-x*b[i]);
    fo(i,1,n)
        fo(j,1,a[i][0]) add(i,a[i][j],inf);
    while (bfs()) ans-=dinic(S,inf);
    if (ans>0) return 1;else return 0;
}
int main() {
    scanf("%d",&n);S=0;T=n+1;
    fo(i,1,n) {
        scanf("%d",&a[i][0]);
        fo(j,1,a[i][0]) scanf("%d",&a[i][j]);
    }
    fo(i,1,n) scanf("%d%d",&b[i],&c[i]);
    db l=0,r=100000,mid;
    while (r-l>1e-6) {
        mid=(l+r)*1.0/2;
        if (check(mid)) r=mid; else l=mid;
    }
    if (l>1e-6) printf("%.6lf",l);
    else printf("CanNotFindTreasure!");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值