H - Binary Tree Gym - 100443H 網絡流

題目鏈接: https://codeforces.com/gym/100443/attachments

題意:

你現在有 n(n<=100)n(n<=100) 節課要上,每節課 xx 有一個上課區間 [lx,rx][l_x,r_x] 和這堂課的學生數量 sxs_x ,每間教室最多可容納 MM 個學生,一節課 uu 在結束後如果想要上另外一節課 vv ,那麼需要等待 Timeu,vTime_{u,v} 的時間(這裏打掃完後還要有一分鐘間隙)。現在問你最少需要借多少教室可以讓所有的課都夠用。

做法:

點的數量這麼少的話就很明顯是網絡流了… 這裏主要介紹兩種做法(隊友的做法和我的做法…)

先說我的做法吧,網上好像比較多的都是這樣的。因爲最大流求的是最大而不是最小,所以我們先把所有教室都分出來,然後用網絡流求有多少教室是可以共用的,即總量-最大共用數,連接 sp>xx+n>tpsp->x,x+n->tp 流量均爲 need[x]need[x] ,前一個表示去借,後一個表示總是能用上的,如果課程 uu 上完可以趕上 vv ,那麼 u>v+nu->v+n 連接流量 infinf ,表示 uu 用的教室可以給 vv 用。 然後跑出來的最大值就是可以共用的。

隊友的做法是用費用流+二分sp>x,x+n>tpsp->x,x+n->tp 連接流量爲 infinf ,費用爲 00 的邊, x>x+nx->x+n 連接流量爲 need[i]need[i] 費用爲 1-1 的邊,表示優先跑過這些邊,然後二分 ssp>spssp->sp 的流量, sspssp 在這裏纔是超級源點,如果跑出來的費用是 sum-sum ,則這個流量是合法的,不斷找更小值。

但是費用流因爲需要二分,所以每次都要重新建圖,所以其實時間應該是更久的(可能也有不完全需要重新建圖的,但好像 暫時想不到)

代碼

最大流做法

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2
using namespace std;
const int maxn=205;
const int maxm=200005;
const int MAX = 1<<26;
typedef long long ll;
typedef pair<int,int> pii;
int nex[maxm],to[maxm],cap[maxm],from[maxm];
int n,m,cnt,head[maxn],d[maxn],sp,tp;//原點,匯點
int wa[maxn][maxn],M;
struct node{
    int l,r,need,id;

}no[maxn];
bool cmp(node a,node b){
    return a.r<b.r;
}
//理論複雜度n2*m
void add(int u,int v,int c){
    from[cnt]=u,to[cnt]=v,cap[cnt]=c,nex[cnt]=head[u],head[u]=cnt++;
    from[cnt]=v,to[cnt]=u,cap[cnt]=0,nex[cnt]=head[v],head[v]=cnt++;
}
int bfs(){
    queue <int> q;
    memset(d,-1,sizeof(d));
    d[sp]=0;
    q.push(sp);
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=nex[i]){
            int u=to[i];
            if(d[u]==-1 && cap[i]>0){
                d[u]=d[cur]+1;
                q.push(u);
            }
        }
    }
    return d[tp] != -1;
}
int dfs(int a,int b){
    int r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=nex[i])
    {
        int u=to[i];
        if(cap[i]>0 && d[u]==d[a]+1)
        {
            int x=min(cap[i],b-r);
            x=dfs(u,x);
            r+=x;
            cap[i]-=x;
            cap[i^1]+=x;
        }
    }
    if(!r)d[a]=-2;
    return r;
}

int dinic(int sp,int tp){
    int total=0,t;
    while(bfs()){
        while(t=dfs(sp,MAX))
        total+=t;
    }
    return total;
}
int main(){
    int T,cas=0; scanf("%d",&T);
    while(T--){
        cnt=0; memset(head,-1,sizeof(head));
        sp=0,tp=201;
        scanf("%d%d",&n,&M);
        int sum=0;
        rep(i,1,n){
            int S; scanf("%d%d%d",&no[i].l,&no[i].r,&S);
            no[i].need=(S+M-1)/M; no[i].id=i;
            sum+=no[i].need;
        }
        rep(i,1,n){
            rep(j,1,n){
                scanf("%d",&wa[i][j]);
            }
        }
        sort(no+1,no+1+n,cmp);
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(no[i].r+wa[no[i].id][no[j].id]<no[j].l){
                    add(i,j+n,MAX);
                }
            }
        }
        for(int i=1;i<=n;i++){
            add(sp,i,no[i].need);
            add(i+n,tp,no[i].need);
        }
        printf("Case %d: %d\n",++cas,sum-dinic(sp,tp));
    }
    return 0;
}

費用流做法

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=205;
const int maxm=200005;
const int inf=0x3f3f3f3f;
const ll finf=-1000;
int dis[maxn],sum;
int vis[maxn],pre[maxn];
int head[maxn],cnt,wa[maxn][maxn];
int n,m,M,k,sp,tp,can[maxn][maxn];
struct node{
    int l,r,need,id;

}no[maxn];
bool cmp(node a,node b){
    return a.r<b.r;
}
ll ans=0;
struct edge{
    int to,cap,cost,next;
}e[maxm];
void add(int from,int to,int cap,int cost){
    e[cnt].to=to; e[cnt].cap=cap;
    e[cnt].cost=cost; e[cnt].next=head[from];
    head[from]=cnt++;

    e[cnt].to=from; e[cnt].cap=0;
    e[cnt].cost=-cost; e[cnt].next=head[to];
    head[to]=cnt++;
}
bool spfa(int s,int t,int &flow,ll &cost){
    queue<int> q;
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    dis[s]=0;  q.push(s);
    vis[s]=1;
    int d=inf;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;
            if(e[i].cap>0&&dis[v]>dis[u]+e[i].cost){
                dis[v]=dis[u]+e[i].cost;
                pre[v]=i;
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(dis[t]==inf){
        return false;
    }
    for(int i=pre[t];~i;i=pre[e[i^1].to]){
        d=min(d,e[i].cap);
    }
    for(int i=pre[t];~i;i=pre[e[i^1].to]){
        e[i].cap-=d;
        e[i^1].cap+=d;
        cost+=(ll)e[i].cost*d;
    }
    flow+=d;
    return true;
}
ll mcmf(int s,int t){
    int flow=0; ll cost=0;
    while(spfa(s,t,flow,cost)){
        //cout<<flow<<" "<<cost<<endl;
    }
    return cost;
}
int ck(int flo){
    memset(head,-1,sizeof(head));
    cnt=0;
    add(sp,tp+1,flo,0);
    for(int i=1;i<=n;i++){
        add(tp+1,i,inf,0);
        add(i,i+n,no[i].need,finf);
        add(i+n,tp,inf,0);
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            if(can[i][j]) add(i+n,j,inf,0);
        }
    }
    ll tmp=mcmf(sp,tp);
    return tmp==finf*sum;
}
int main(){
    int T,cas=0; scanf("%d",&T);
    while(T--){

        memset(can,0,sizeof(can));
        ans=0;
        sp=0;tp=201;
        scanf("%d%d",&n,&M);
        int L=0,R=0;
        rep(i,1,n){
            int S; scanf("%d%d%d",&no[i].l,&no[i].r,&S);
            no[i].need=(S+M-1)/M; no[i].id=i;
            R+=no[i].need;
        }
        sum=R;
        rep(i,1,n){
            rep(j,1,n){
                scanf("%d",&wa[i][j]);
            }
        }
        sort(no+1,no+1+n,cmp);
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(no[i].r+wa[no[i].id][no[j].id]<no[j].l) can[i][j]=1;
            }
        }
        int ans=R;
        while(L<=R){
            int mid=(L+R)/2;
            if(ck(mid)){
                ans=mid;
                R=mid-1;
            }
            else L=mid+1;
        }
        printf("Case %d: %d\n",++cas,ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章