1. 程式人生 > >【JZOJ B組】【GDOI2016模擬】作業分配

【JZOJ B組】【GDOI2016模擬】作業分配

Description

暑假裡,總有某些同學由於貪玩而忘記做作業。這些人往往要等到暑假快結束時才想起堆積如山的作業,但在這最後幾天的時間裡把這些作業做完已經不太現實了,於是“志同道合”的他們想出了一個妙招。
假設現在有n科作業,他們把第i科作業按作業量平均分成ai份,他們總共有m個人,第j個人只願意做其中任意的bj份作業,而且我們知道ai的和等於bj的和,以及把第i科作業的其中一份給第j個人做的時間是ci,j。現在他們想分配下各自的任務,一起把作業做完,然後再%#^&%^&#%%&^
現在的問題來了,他們希望所有人做作業的總時間的和最小,你能幫助他們解決問題嗎?

Input

輸入檔案的第一行有兩個n,m表示有多少科作業和多少個人,第二行有n個數依次表示ai,第三行有m個數依次表示bj,最後n行,每行m個數表示ci,j。

Output

輸出檔案包含一行為最少的時間總和。

Sample Input

2 2
3 5
5 3
1 2
2 1

Sample Output

10
【樣例解釋】
第1個人做完所有的第1科作業以及第2科作業的其中2份,第2個人把第2科另外3份做完。

Data Constraint

第一個點 n<=5 m<=5 ai,bi<=15 sum(ai)<=20
第二個點 n<=10 m<=10 ai,bi<=20 sum(ai)<=100
第三個點 n<=30 m<=30 ai,bi<=600 sum(ai)<=10000
第四個點到第十個點 n<=200 m<=200 ai,bi<=10000 sum(ai)<=1000000

思路

顯然用最小費用最大流啦!

首先,原點向每一個人連一條流量為b[i],權值為0的邊,表示每一個人願意做多少作業。
然後,每一項作業向匯點連一條流量為a[i],權值為0的邊,表示總共有多少作業。
最後,每個人與作業連流量為inf,權值為c[i][j]的邊,表示每個人做作業所用時間。

然後,你會TLE!!

為什麼呢?想想看,我們總共練了2n^2+2n條邊(加上反向邊),費用流複雜度=m^2,肯定會TLE啊!

於是dalao說要用動態開點。
大致思路就是:一開始只加一部分邊跑EK,如果一些邊滿流(根據題目確定),就加進新邊,繼續跑EK,如此迴圈往復。對於這道題,我們用陣列E[i]存下與左邊第i個點與右邊所有的點所連的邊,對每個E[i]按照花費從小到大排序。先把左邊每個點i的E[i]中花費最小的邊加進圖,然後跑EK。記E[i][j].to表示i的第j條邊所連線的右邊的點,E[i][j].dist表示i的第j條邊的花費,cnt[i]表示i上一次加進去的是第幾條邊。接著檢查左邊每個點i,如果E[i][cnt[i]].to連向T的邊滿流時,我們必須新增新邊,不然有可能無法增廣。於是就從cnt[i]開始從小到大加邊,一直到E[i][cnt[i]].to連向T的邊沒有滿流或者已經全部加完為止。加完以後繼續跑EK。dalao說的

so,這改了我一個晚上。。。

程式碼

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10077,inf=0x3f3f3f3f;
int n,m,cnt=1,last[maxn],t,f[maxn],d[maxn],s,a[510],b[510],k[maxn],ss[maxn],ans,dis[maxn],w[maxn],mx;
struct C
{
    int d,v;
}c[510][510];
struct E
{
    int to,next,flow,cost;
}e[maxn*30];
bool cmp(C x,C y) 
{ 
    return x.v<y.v; 
}
void add(int u,int v,int x,int y)
{
    e[++cnt].to=v; e[cnt].flow=x; e[cnt].cost=y; e[cnt].next=last[u]; last[u]=cnt; 
    e[++cnt].to=u; e[cnt].flow=0; e[cnt].cost=-y; e[cnt].next=last[v]; last[v]=cnt;
}
bool spfa()
{
    queue<int> q;
    for (int i=s;i<=t;i++)
    {
        dis[i]=inf;
        f[i]=d[i]=0;
    }
    q.push(s);
    f[s]=1; dis[s]=0; w[s]=inf;
    while (!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=last[u]; i; i=e[i].next)
            if(e[i].flow&&e[i].cost+dis[u]<dis[e[i].to])
            {
                dis[e[i].to]=e[i].cost+dis[u];
                w[e[i].to]=min(w[u],e[i].flow);
                d[e[i].to]=i;
                if (!f[e[i].to])
                {
                    f[e[i].to]=1;
                    q.push(e[i].to);
                }   
            }
        f[u]=0;     
    }
    if (dis[t]<inf) return 1; else return 0;
}
void mcmf()
{
    int x=t;
    while (x!=s)
    {
        int i=d[x];
        e[i].flow-=w[t];
        e[i^1].flow+=w[t];
        x=e[i^1].to;
    }
    mx+=w[t];
    ans+=dis[t]*w[t];
}
void solve()
{
    while (spfa())
    {
        mcmf();
        for(int i=1; i<=n; i++)
            while (!e[k[c[i][ss[i]].d]].flow&&ss[i]<m) 
            {
                ss[i]++;
                add(i,n+c[i][ss[i]].d,a[i],c[i][ss[i]].v);
                //,printf("add %d to %d\n",i,c[i][ss[i]].d+n);
            }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    s=0; t=m+n+1;
    for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    for(int i=1; i<=m; i++) scanf("%d",&b[i]);
    for(int i=1; i<=n; i++)
    {
        for (int j=1; j<=m; j++) scanf("%d",&c[i][j].v),c[i][j].d=j;
        sort(c[i]+1,c[i]+m+1,cmp);
        ss[i]=1;
        add(i,n+c[i][1].d,a[i],c[i][1].v);
    }
    for(int i=1; i<=n; i++) add(s,i,a[i],0);
    for(int i=1; i<=m; i++) add(n+i,t,b[i],0),k[i]=cnt-1;
    solve();
    printf("%d",ans);
    return 0;
}