1. 程式人生 > 其它 >P3749 [六省聯考 2017] 壽司餐廳 分析

P3749 [六省聯考 2017] 壽司餐廳 分析

最大權閉合子圖 的模型

模型:有若干個物品,每種物品有一個價值 $v_i$,選取了物品就意味著獲得了價值。

物品之間有限制關係:$x \to y$ 表示若要選擇物品 $x$ 則必須選擇物品 $y$。目標是最大化價值和。

解法

考慮使用最小割解決此類問題:

將每個物品與源點 $s$ 和匯點 $t$ 相連。若割掉與 $s$ 相連的邊表示不選這個物品,割掉與 $t$ 相連的邊表示選擇這個物品。

對於一個物品的價值 $v$,如果 $v>0$ 則令它與 $s$ 相連的邊的權值為 $v$,與 $t$ 相連的邊的權值為 $0$,將 $v$ 加入答案。表示不選擇這個物品會付出 $v$ 的代價;如果 $v<0$ 則令它與 $s$ 相連的邊的權值為 $0$,與 $t$ 相連的邊的權值為 $-v$,表示選擇這個物品會付出 $-v$ 的代價。

對於 $x \to y$ 的關係,轉化為 $x$ 向 $y$ 連一條權值極大的邊,顯然這條邊永遠不會被割,如果選擇了 $x$,即割掉 $x$ 與 $t$ 相連的邊,那麼如果不選 $y$,即割掉 $y$ 與 $s$ 相連的邊,就會出現路徑 $s \to x \to y \to t$,所以必須選擇 $y$。而如果不選 $x$ 則對 $y$ 的選擇沒有影響。

至此,問題符合使用 Dinic 演算法解決網路流的條件,結合最大流-最小割定理,可以使用 Dinic 演算法解決此類問題。

轉化

在本題中,吃了每種型別為 $x$ 的壽司需要付出 $x$ 的代價,而吃過型別為 $x$ 的壽司需要付出 $mx^2$ 的代價。考慮將每種 $d_{i,j}$ 的收益看成物品,顯然如果選擇 $d_{i,j}$($i<j$),則必須選擇 $d_{i,j-1}$ 以及 $d_{i+1,j}$。

至此,轉化完成,可以用最大權閉合子圖的模型求解。

namespace LZX
{
    using namespace std;
    typedef long long i64;
    const int MAXN=6060,MAXM=16060;
    const i64 INF=0x7fffffffffffffff;
    int s,t,cnt=1,head[MAXN],to[MAXM<<1],nxt[MAXM<<1],pre[MAXN],dep[MAXN],d[105][105],id[105][105],a[105];
    i64 f[MAXM<<1];
    void g_flow_add(int a,int b,i64 c)
    {
        nxt[++cnt]=head[a];
        head[a]=cnt;
        to[cnt]=b;
        f[cnt]=c;
        nxt[++cnt]=head[b];
        head[b]=cnt;
        to[cnt]=a;
        f[cnt]=0;
    }
    bool g_flow_BFS()
    {
        int u,v;
        queue<int> q;
        memset(dep,0,sizeof(dep));
        dep[s]=1;
        q.push(s);
        do
        {
            u=q.front();
            q.pop();
            for(int i=head[u];i;i=nxt[i])
            {
                v=to[i];
                if(f[i]&&!dep[v])
                {
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        while(!q.empty());
        return dep[t];
    }
    i64 g_flow_DFS(int u,i64 flow)
    {
        i64 res,v,temp;
        if(u==t)
        {
            return flow;
        }
        res=0;
        for(int &i=pre[u];i;i=nxt[i])
        {
            v=to[i];
            if(f[i]&&dep[v]==dep[u]+1)
            {
                temp=g_flow_DFS(v,min(flow,f[i]));
                f[i]-=temp;
                f[i^1]+=temp;
                flow-=temp;
                res+=temp;
                if(!flow)
                {
                    break;
                }
            }
        }
        return res;
    }
    i64 g_flow_dinic()
    {
        i64 res=0;
        while(g_flow_BFS())
        {
            memmove(pre,head,sizeof(head));
            res+=g_flow_DFS(s,INF);
        }
        return res;
    }
    int _main()
    {
        int n,m,k,maxa,cost;
        i64 ans=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            maxa=max(maxa,a[i]);
        }
        s=1;
        t=2;
        k=2;
        for(int i=1;i<=n;i++)
        {
            for(int j=i;j<=n;j++)
            {
                scanf("%d",&d[i][j]);
                id[i][j]=++k;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=i;j<=n;j++)
            {
                cost=d[i][j];
                if(i==j)
                {
                    if(m)
                    {
                        g_flow_add(id[i][j],a[i]+k,INF);
                    }
                    cost-=a[i];
                }
                else
                {
                    g_flow_add(id[i][j],id[i+1][j],INF);
                    g_flow_add(id[i][j],id[i][j-1],INF);
                }
                if(cost>0)
                {
                    g_flow_add(s,id[i][j],cost);
                    ans+=1ll*cost;
                }
                else if(cost<0)
                {
                    g_flow_add(id[i][j],t,-cost);
                }
            }
        }
        if(m)
        {
            for(int i=1;i<=maxa;i++)
            {
                g_flow_add(++k,2,i*i);
            }
        }
        printf("%lld\n",ans-g_flow_dinic());
        return 0;
    }
}