1. 程式人生 > >【學術篇】網絡流24題——方格取數加強版

【學術篇】網絡流24題——方格取數加強版

發現 什麽 img 水題 clas 自動補全 details com ()

將近一年沒寫(別問我為啥)網絡流都忘光啦OvO現在決定來重拾一下...
所以從哪道題開始哩?

就決定是你了!

"等等, 這不是個dp大水題麽?"
不好意思 放錯鏈接了 應該是這個

就決定是你了!

Emmmm很明顯唯一的區別就是把2改為了\(k\)... 可我們總不能跑一個\(2k\)維dp吧? 而且還不知道\(k\)是多少...
那咋整呢...賣什麽關子啊←_←標題都說是網絡流了還用想別的?

我們就考慮網絡流吧... 網絡流主要的問題就是建圖啦~
建圖的時候就是考慮題目中的各個條件怎麽轉化為邊的限制.
可以把每個點拆成兩個然後建邊.

  • 在拆開的兩個點之間建一條費用為\(a[i][j]\)
    , 流量為1的邊
    , 表示這個權值可以且僅可以取一次.
  • 再在拆開的兩個點之間建一條費用為0, 流量為\(\infty\)的邊, 表示這個點可以走很多次, 但是走的後幾次是取不到權值的.
  • 每個點的右邊的點向右邊或下邊相鄰的點的左邊連一條費用為0, 流量為\(\infty\)的邊, 就是表示從這個點往下/往右走了啊
  • 從源點\(S\)向左上角點的左邊連一條費用為0, 流量為\(k\)的邊, 表示一共有\(k\)次要走.
  • 從匯點\(T\)向右下角點的右邊連一條費用為0, 流量為\(k\)的邊, 表示走完.
  • (不過好像源點和匯點連的邊只要有一個流量是\(k\)起到限制作用, 另一個寫\(\infty\)或別的什麽比\(k\)
    大的數也可以?

以樣例為例我們建出來的圖應該長這樣(由於從\(S\)出發只有一條邊我就橫過來了~~
技術分享圖片

這樣圖就建好了, 然後我們跑一遍最大費用最大流就好了... 但是這玩意怎麽跑呢?
把每條邊的費用設置成相反數然後跑最小費用最大流不就完了麽←_← 最後結果是個負的再倒過來不就好了...
(所以這種方法不能用來處理負權... 所以題目給的都是正整數...

之後貼板子就好了... 不過再扯點別的...
比如我忘記費用流怎麽寫了於是決定直接去學zkw費用流..
之前也看過zkw(orz)的blog但是個人來講並不是很喜歡裏面的風格OvO
然後baidu一下"zkw費用流"發現第一條是一位dalao改的zkw費用流,(講的挺好的大家都去學就好了, 我就不發表自己的拙見了)我就直接按自己的碼風改(chao)了一遍, 去刷了一下模板題... 本來昨天晚上想發出來的結果光顧著頹廢了就

耽擱了..
那就不用發板子了直接跟著題貼出來就好咯...
不過寫起來和dinic是真的像..(正好復習了一下同樣忘光的dinic..

代碼:

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=5050;
const int M=30050;
const int INF=0x7f7f7f7f;
inline int gn(int a=0,char c=0){
    for(;c<'0'||c>'9';c=getchar());
    for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}
struct edge{
    int to,next,cost,flow;
}e[M]; int v[N],tot=1;
void buildedge(int x,int y,int cost,int flow){
    e[++tot].to=y; e[tot].next=v[x]; v[x]=tot; e[tot].cost=cost; e[tot].flow=flow;
    e[++tot].to=x; e[tot].next=v[y]; v[y]=tot; e[tot].cost=-cost; e[tot].flow=0;
}
int a[52][52],d[N],s,t,n,k,cost;
bool vis[N];
queue<int> q;
inline bool spfa(const int& s,const int& t){
    memset(vis,0,sizeof(vis));
    memset(d,0x7f,sizeof(d));
    vis[t]=1; d[t]=1; q.push(t);
    while(!q.empty()){
        int x=q.front(); q.pop(); vis[x]=0;
        for(int i=v[x];i;i=e[i].next)
            if(e[i^1].flow&&d[e[i].to]>d[x]-e[i].cost){
                d[e[i].to]=d[x]-e[i].cost;
                if(!vis[e[i].to]) 
                    q.push(e[i].to),vis[e[i].to]=1;
            }
    }
    return d[s]<INF;
}
int dfs(int x,int mx,int s=0){ vis[x]=1;
    if(x==t) return mx; int k;
    for(int i=v[x];i;i=e[i].next)
        if(!vis[e[i].to]&&e[i].flow&&d[e[i].to]==d[x]-e[i].cost){
            k=::dfs(e[i].to, ::min(e[i].flow, mx-s));
            if(k) cost+=k*e[i].cost,e[i].flow-=k,e[i^1].flow+=k,s+=k;
            if(s==mx) break;
        }
    return s;
}
int mcmf(int flow=0){
    while(::spfa(s, t)){ vis[t]=1;
        while(vis[t]){
            memset(vis,0,sizeof(vis));
            flow+=dfs(s,INF);
        }
    } return flow;
}
int main(){ n=gn(),k=gn(); 
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            a[i][j]=gn();
    int nn=n*n; s=0; t=nn<<1|1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j){
            int x=(i-1)*n+j;
            ::buildedge(x, x+nn, -a[i][j], 1);
            ::buildedge(x, x+nn, 0, INF);
            if(i<n) ::buildedge(x+nn, x+n, 0, INF);
            if(j<n) ::buildedge(x+nn, x+1, 0, INF);
        }
    ::buildedge(s, 1, 0, k); ::buildedge(t-1, t, 0, k);
    mcmf(); printf("%d",-cost);
}

結果就這麽個昨天剛學的板子今天再寫就寫疵了.. mdzz..

這個板子稍微改改可以水掉的題目(換句話說數據比較適合爆炸的程序對拍的:

  • luogu2405 (這個不用改2333
  • luogu1004 (把k=gn()改成k=2, 然後把讀入一改就好
  • luogu1006 (還是k=2, 改改讀入就行 這倆好像本來就是一個題吧?

解釋一下代碼裏面出現的過多的::是用來觸發clang-complete的自動補全的...
因為可以同時補全參數類型.. 超級好用的一個功能, 那麽為什麽不用呢XD
所以就多出來很多::又懶得刪了 反正也不會CE, 就這樣唄OvO

【學術篇】網絡流24題——方格取數加強版