牛客:动物森友会(网络流+二分答案)

题意:

链接:https://ac.nowcoder.com/acm/contest/5278/L
来源:牛客网

Compute 最近开始玩动物森友会了。
这个游戏的时间与现实时间是同步的(一周有 7 天),而一些特定事件只会在一周的某些天解锁。
我们假设有 n 个不同的事件,而每个事件都会给予不同的材料,并且每个事件只会在一周中的特定几天开放,在开放的时间内可以完成多次。但由于 Compute 要参加训练,他每天并没有多少时间玩游戏,所以他每天最多只能完成 e 次事件。
现在 Compute 想做出一件非常稀有的道具——高达,并且他计算出了他收集齐所有材料需要完成每一种事件的次数。

假设现在 Compute 从周一开始玩这个游戏,他最少需要经过几天(包括不玩游戏的日子)才能造出高达?

题解:

/*
 *动物森友会
 *题意:
 *一周有7天
 *有n个不同的事件
 *一个事件只会在一周中的特定几天开放
 *在开放的时间可以完成多次
 *每天最多完成e次
 *每件事情有一个目标次数
 *从周一开始玩,最少需要几天才能达到目标
 *题解:
 *最小费用最大流的想法
 *源点向所有事件连边,容量为目标次数
 *所有事件向一周的7天连边,如果这个事件在当天能做
 *容量为inf
 *一周中的所有天向汇点连边,容量为e*当天在天数中出现的次数
 *二分天数
 *天数上界是1e18
 */
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
const ll inf=1e18;
struct node {
    int u,v,nxt;
    ll w;
}edge[maxn*2];
int head[maxn];
int tot;
void addedge (int u,int v,ll w) {
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].nxt=head[u];
    head[u]=tot++;
    
    edge[tot].u=v;
    edge[tot].v=u;
    edge[tot].w=0;
    edge[tot].nxt=head[v];
    head[v]=tot++;
} 

int c[maxn];
int is[maxn][7];//事件i在第j天是否开放 
int n,e;

ll dep[maxn];
ll inq[maxn];
ll cur[maxn];
int wjm;
ll maxflow=0;
int s,t;
bool bfs () {
    for (int i=0;i<=t;i++) {
        cur[i]=head[i];
        dep[i]=inf;
        inq[i]=0;
    }
    dep[s]=0;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        inq[u]=0;
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (dep[v]>dep[u]+1&&edge[i].w) {
                dep[v]=dep[u]+1;
                if (inq[v]==0) {
                    q.push(v);
                    inq[v]=1;
                } 
            }
        }
    }
    if (dep[t]!=inf) return 1;
    return 0;
}
ll dfs (ll u,ll flow) {
    ll increase=0;
    if (u==t) {
        wjm=1;
        maxflow+=flow;
        return flow;
    }
    ll used=0;
    for (int i=cur[u];i!=-1;i=edge[i].nxt) {
        cur[u]=i;
        int v=edge[i].v;
        if (edge[i].w&&dep[v]==dep[u]+1) {
            if (increase=dfs(v,min(flow-used,edge[i].w))) {
                used+=increase;
                edge[i].w-=increase;
                edge[i^1].w+=increase;
                if (used==flow) break;
            }
        }
    }
    return used;
}
ll Dinic () {
    maxflow=0;
    while (bfs()) {
        wjm=1;
        while (wjm==1) {
            wjm=0;
            dfs(s,inf);
        }
    }
    return maxflow;
}



ll check (ll mid) {
    //mid表示天数
    //0为源点
    //1~n为事件点
    //n+1~n+7表示天
    //n+8表示汇点
    for (int i=0;i<maxn;i++) head[i]=-1;
    tot=0;
    s=0;
    t=n+8;
    for (int i=1;i<=n;i++) addedge(s,i,c[i]);
    for (int i=1;i<=n;i++) {
        for (int j=1;j<=7;j++) {
            if (is[i][j]) {
                addedge(i,j+n,inf);
            }
        }
    }
    for (int i=1;i<=7;i++) addedge(i+n,t,(mid/7+(mid%7>=i))*e);
    ll ans=Dinic();
    return ans;
}

int main () {
    scanf("%d%d",&n,&e);
    int sum=0;
    for (int i=1;i<=n;i++) {
        scanf("%d",c+i);
        sum+=c[i];
        int m;
        scanf("%d",&m);
        for (int j=1;j<=m;j++) {
            int x;
            scanf("%d",&x);
            is[i][x]=1;
        }
    }
    ll l=1,r=1e9;
    ll u=-1;
    while (l<=r) {
        ll mid=(l+r)>>1;
        if (check(mid)==sum) {
            u=mid;
            r=mid-1;
        }
        else {
            l=mid+1;
        }
    }
    printf("%lld\n",u);
}

 

posted @ 2020-08-26 16:22  zlc0405  阅读(112)  评论(0编辑  收藏  举报