【學術篇】網絡流24題——方格取數加強版
將近一年沒寫(別問我為啥)網絡流都忘光啦OvO現在決定來重拾一下...
所以從哪道題開始哩?
就決定是你了!
"等等, 這不是個dp大水題麽?"
不好意思 放錯鏈接了 應該是這個
就決定是你了!
Emmmm很明顯唯一的區別就是把2改為了\(k\)... 可我們總不能跑一個\(2k\)維dp吧? 而且還不知道\(k\)是多少...
那咋整呢...賣什麽關子啊←_←標題都說是網絡流了還用想別的?
我們就考慮網絡流吧... 網絡流主要的問題就是建圖啦~
建圖的時候就是考慮題目中的各個條件怎麽轉化為邊的限制.
可以把每個點拆成兩個然後建邊.
- 在拆開的兩個點之間建一條費用為\(a[i][j]\)
- 再在拆開的兩個點之間建一條費用為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題——方格取數加強版