【zjoi2010】贪吃的老鼠(网络流)

时间限制:10秒 内存限制:512M
【问题描述】

  奶酪店里最近出现了 m 只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉。奶酪店中一天会生产n块奶酪,其中第 i块的大小为pi,会在第ri秒被生产出来,并且必须在第di秒之前将它吃掉。第 j 只老鼠吃奶酪的速度为 sj,因此如果它单独吃完第 i 快奶酪所需的时间为pi/sj。老鼠们吃奶酪的习惯很独特,具体来说:

  (1) 在任一时刻,一只老鼠最多可以吃一块奶酪;
  (2) 在任一时刻,一块奶酪最多被一只老鼠吃。

  由于奶酪的保质期常常很短,为了将它们全部吃掉,老鼠们需要使用一种神奇的魔法来延长奶酪的保质期。将奶酪的保质期延长 T秒是指所有的奶酪的 di变成di+T。同时,使用魔法的代价很高,因此老鼠们希望找到最小的T 使得可以吃掉所有的奶酪。

【输入格式】

  输入文件的第一行包含一个整数 K,表示输入文件中数据的组数。
  每组数据的第一行包含两个整数 n 和 m,分别表示奶酪和老鼠的数量。接下来的 n 行每行包含三个整数pi,ri,di。最后m行每行包含一个整数,表示 sj。 pi,ri,di,sj的含义如上文所述。

【输出格式】

  输出文件中包含K行,每行包含一个实数,表示你找到的最小的T。你的答案和标准答案的绝对误差不应超过0.0001。

【输入样例】

2 2 2
13 0 4
10 1 3
4
2
1 1
1 0 2
1

【输出样例】

0.5
0

【样例解释】

 第一组数据中:
  第0到第1秒:第一只老鼠吃第一块奶酪;
  第1到第3.5 秒:
 -第一只老鼠吃第二块奶酪;
 -第二只老鼠吃第一块奶酪;
  第3.5 到第4.5秒:第一只老鼠吃第一块奶酪。

【数据范围】

30%的数据中,1≤n,m≤5;
100%的数据中, 1≤k≤5,1≤n,m≤30,1≤pi≤10^5, 0≤ri < di ≤10^7, 1≤sj≤10^5。

这道题真的可以想到网络流吗?(一直在想暴力的,以为是记忆化+hash)

这道题第一个难点是想到要用网络流,第二个难点在于想到也没什么用(建图的方法太难,也许只是我太弱。)

二分猜T就不说了都能想到,现在问题变成了判定能否吃完了。

如果每个奶酪可以有很多老鼠一起吃那就好办,老鼠按时间分成2n份,然后每个老鼠(分完后的)向它所在区间能吃到的奶酪连边就好,然后s到老鼠连流量为 区间长度*速度的边,奶酪往t连流量为大小的边,跑网络流就好。

但这道题不行,因为一个奶酪不能同时被多个老鼠吃。

我们考虑把老鼠的速度用差分表示,s[j]=s[j]-s[j+1](s先从大到小排序)

然后还是像上面说的那么连边,只是s到老鼠的边的流量变为:s[j]*区间长度*j.
因为是差分序列,所以前面速度大的老鼠的部分流量实际上是从后面的老鼠那里流过去的,所以要乘以j。

代码如下:(重点就在建图)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=3005;
const double inf=200000005;
const double eps=1e-8;

struct edge
{
    int u,v,next;
    double f,c;
}e[maxn*100];
struct shu
{
    double p,x,y;
}a[maxn];
int n,m,f[maxn],cnt=-1,d[maxn],cur[maxn],q[maxn*maxn],s,t,tot;
double v[maxn],time[maxn];

void add(int u,int v,double F){
    e[++cnt]=(edge){u,v,f[u],F,0};f[u]=cnt;
    e[++cnt]=(edge){u,v,f[v],0,0};f[v]=cnt;
}

bool cmp(int a,int b){
    return a>b;
}

void init(){
    scanf("%d%d",&n,&m);
    tot=0;
    for(int i=1;i<=n;i++) 
    {
        scanf("%lf%lf%lf",&a[i].p,&a[i].x,&a[i].y);
        tot+=a[i].p;
    }
    for(int i=1;i<=m;i++) scanf("%lf",&v[i]);

    sort(v+1,v+1+m,cmp);
    for(int i=1;i<m;i++) v[i]=v[i]-v[i+1];
}

bool bfs(){
    memset(d,0,sizeof(d));
    int frond=0,root=0;
    d[s]=1;
    q[frond++]=s;
    while(frond!=root)
    {
        int i=q[root++];
        for(int k=f[i];k!=-1;k=e[k].next)
        {
            int j=e[k].v;
            if(d[j]||e[k].f==e[k].c) continue;
            d[j]=d[i]+1;
            q[frond++]=j;
        }
    }
    return d[t]>0;
}

double dfs(int i,double a){
    if(i==t||a==0) return a;
    double F,flow=0;
    for(int &k=cur[i];k!=-1;k=e[k].next)
    {
        int j=e[k].v;
        if(d[j]==d[i]+1&&(F=dfs(j,min(a,e[k].f-e[k].c)))>0)
        {
            flow+=F;
            a-=F;
            e[k].c+=F;
            e[k^1].c-=F;
            if(a==0) break;
        }
    }
    return flow;
}

double dinic(){
    double flow=0;
    while(bfs())
    {
        memcpy(cur,f,sizeof(cur));
        flow+=dfs(s,inf);
    }
    return flow;
}

bool check(double T){
    cnt=0;
    s=0,t=n+n*m*2+1;
    for(int i=s;i<=t;i++) f[i]=-1;

    for(int i=1;i<=n;i++) 
    {
        add(s,i,a[i].p);
        time[i]=a[i].x;
        time[i+n]=a[i].y+T;
    }
    sort(time+1,time+2*n+1);

    for(int i=2;i<=2*n;i++)
    {
        double x=time[i]-time[i-1];
        if(x<eps) continue;
        for(int j=1;j<=m;j++)
        {
            int y=n+(i-1)*m+j;
            add(y,t,j*v[j]*x);
            for(int k=1;k<=n;k++)
            {
                if(time[i-1]-a[k].x>-eps&&time[i]-a[k].y-T<eps)
                add(k,y,v[j]*x);
            }
        }
    }
    return dinic()-tot>-eps;
}

int main()
{
    freopen("in.txt","r",stdin);
    int  T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        double A=0,B=10000005,ans;
        while(B-A>=0.001)
        {
            double C=(A+B)/2;
            if(check(C)) B=C,ans=C;
            else A=C;
        }
        printf("%.1lf\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值