SRM 676 div1 hard

題目大意

n 種植物,每種植物生長需要ti 的時間,當然了,植物可以同時生長。現在有若干條限制xi,yi ,表示植物yi 要等xi 生長完了他才能開始生成長。對於每種植物,可以用ci 的代價使其生長時間減1,可以重複使用,但最多減爲0。問最少要多少時間所有植物才能長完。
n50

題解

這題很難。首先假設有兩種超級植物S,T ,表示S 要一開始長,T 要最後長。然後我們可以二分答案,設爲λ ,接下來,對於每種植物,設xi 表示他開始長的時間,yi 表示他結束生長的時間,di 表示減了多少時間。那麼可以列出約束

ys+λxixtyixi+dixixtysyixiyiyj(ji)

目標函數是
minimize{di×ci}

接下來需要考慮一些經典的對偶模型。對於最大循環費用流,其線性規劃模型是

fi,jjfi,jbi,j=jfj,imaximize{fi,j×costi,j}

這個東西的對偶,就是
vi,j+ϕiϕjcosti,jminimize{vi,j×bi,j}

可以發現這個對偶問題貌似能套在原問題上。
比如對於這條式子

yixi+diti

是可以直接套的,這相當於在循環流中連上一條xiyi 的流量爲ci 費用爲ti 的邊。
那麼對於類似xiyj0 這樣的限制,我們只能強行給他加上一個變量p 來湊xiyj+p0 ,但必須滿足p=0 ,相當於在目標函數中p 的係數爲+ ,那麼在循環流中就相當於連上一條yjxi 流量爲+ 費用爲0的邊。

最後的問題是,給定圖G,如何求一個最大循環流。

最大循環流

這個問題等價於我們用若干個環,使得其覆蓋的權值和最大。
一個點在環上,等價於其入度=出度。
那麼我們一開始先貪心的把所有權值爲正數的邊都選上,同時修改兩端的度數。對於一條負邊權(u,v,f,c) ,相當於我們可以以c 的代價使得deg[u]+1deg[v]1 ,同理,對於一條正邊權,我們可以把他刪掉,相當於使deg[v]+1deg[u]1 。最終要使得所有的deg 都爲0。這就變成了經典的費用流模型了。

代碼

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 1005;

typedef long long ll;

class network
{
public:
    struct node
    {
        ll to,next,flow,cost;

        node(void){}
        node(int a,int b,ll c,ll d) : to(a),next(b),flow(c),cost(d){}
    }e[maxn * 100];

    ll deg[maxn];
    int final[maxn],ed[maxn][4],n,tot,s,t,cnt;

    void set(int siz)
    {
        n = siz,s = n,t = n + 1;
        for(int i = 0;i <= t;i ++) final[i] = deg[i] = 0;
        tot = 1;
        cnt = 0;
    }

    void link(int u,int v,ll f,ll c)
    {
        e[++ tot] = node(v,final[u],f,c),final[u] = tot;
        e[++ tot] = node(u,final[v],0,-c),final[v] = tot;
    }

    void push(int u,int v,int f,int c)
    {
        ed[++ cnt][0] = u,ed[cnt][1] = v,ed[cnt][2] = f,ed[cnt][3] = c;
    }

    ll maxflow()
    {
        static ll dis[maxn];
        static int pr[maxn];
        static bool in[maxn];
        ll ret = 0;
        for(;;)
        {
            for(int i = 0;i <= t;i ++) dis[i] = (1ll << 60),in[i] = 0;
            dis[s] = 0;
            static int que[maxn * 100];
            que[1] = s;
            in[s] = 1;
            for(int fi = 1,en = 1;fi <= en;fi ++)
            {
                int u = que[fi];
                for(int i = final[u];i;i = e[i].next)
                    if (e[i].flow > 0 && dis[e[i].to] > dis[u] + e[i].cost)
                    {
                        dis[e[i].to] = dis[u] + e[i].cost;
                        pr[e[i].to] = i;
                        if (!in[e[i].to])
                        {
                            in[e[i].to] = 1,que[++ en] = e[i].to;
                        }
                    }
                in[u] = 0;
            }
            if (dis[t] == (1ll << 60)) break;
            ll fl = (1ll << 60);
            for(int i = t;i != s;i = e[pr[i] ^ 1].to) 
                fl = min(fl,e[pr[i]].flow);
            ret += fl * dis[t];
            for(int i = t;i != s;i = e[pr[i] ^ 1].to)
                e[pr[i]].flow -= fl,e[pr[i] ^ 1].flow += fl;
        }
        return ret;
    }

    ll calc()
    {
        ll sum = 0;
        for(int i = 1;i <= cnt;i ++)
        {
            ll u = ed[i][0],v = ed[i][1],f = ed[i][2],c = ed[i][3];
            if (c < 0)
                link(u,v,f,-c); else
                {
                    sum += f * c;
                    deg[u] += f,deg[v] -= f;
                    link(v,u,f,c);
                }
        }
        for(int i = 0;i < n;i ++) 
            if (deg[i] < 0) link(s,i,-deg[i],0); else link(i,t,deg[i],0);
        return sum - maxflow();
    }
}flow;

class Farmville
{
public:
    int minTime(vector<string> s, vector<int> t, vector<int> c,int budget)
    {
        int l = 0,r = 100000,tmp,n = t.size();
        for(int mid;l <= r;)
        {
            mid = l + r >> 1;
            flow.set(n * 2 + 2);
            int ys = n * 2,xt = n * 2 + 1;
            for(int i = 0;i < n;i ++)
            {
                flow.push(ys,i << 1,1 << 30,0),flow.push(i << 1 | 1,xt,1 << 30,0);
                flow.push(i << 1,i << 1 | 1,1 << 30,0),flow.push(i << 1,i << 1 | 1,c[i],t[i]);
            }
            for(int i = 0;i < n;i ++)
                for(int j = 0;j < n;j ++)
                    if (s[i][j] == '1') flow.push(j << 1 | 1,i << 1,1 << 30,0);
            flow.push(xt,ys,1 << 30,-mid);
            if (flow.calc() > budget) l = mid + 1; else
                tmp = mid,r = mid - 1;
        }
        return tmp;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章