「P4313」文理分科 解題報告
阿新 • • 發佈:2019-01-08
題面
大意:\(n \times m\) 矩陣上的點,分成兩部分,每個點分配後會有一個分數,上下左右以及中間 的五個點分到一起會有額外的分數,可以是0,求分配後最大的分數
思路:
網路流最小割
網路流的模板並不是很難,難在建邊
而對於這道題,其實是網路流中的一種標準模型吧
一個點分到A或B兩部分中,不同的點組合在一起有加分的情況
用網路流的最小割做
就應該先把問題轉化一下:
最優的答案當然是所有分值都加在一起咯
但是,一個點不可能同時出現在兩部分,所以導致了一些不可能的情況
現在我們的目標就是去掉儘量少的分數(邊權),使一個點只能出現在一個部分
這就非常符合最小割了
可以一開始把每個點拆成兩個點,一個和超級源點S,另一個和超級匯點T相連
在題目中的實際意義就是這個同學是學文還是學理,邊權為 他/她 的滿意度,記得在這兩個點之間連上一條邊權為INF的邊,因為同一點是不能分割的!
然後處理組合,
對於每個組合,我們新建一個結點,然後把它當做一個普通的節點處理
RT:
1和2是兩個普通點,為了表示1和2的組合關係,就引入結點3當做 偽1結點 和 偽2結點,與\(1'\)和\(2'\)連邊
\(3'\) 做同樣的處理
這幅圖和上面的圖是等價的,但是從意義上來看,上面的圖更能說明 \(3\) 和 \(3'\) 的意義
建圖之後跑一遍Dinic就好了
由於我們求的是最小割
所以結果應該是Ans——所有情況的滿意度總和,減去Maxflow——至少刪去多少邊,才能滿足一個同學只能選一科
Warming:新建結點並連邊的時候,不要忘記與當前的點相連
Code:
#include<bits/stdc++.h> #define INF 0x7f7f7f7f #define M 200010 #define N 40010 #define Mn 110 using namespace std; int Cx[4]={1,0,0,-1};//上下左右 int Cy[4]={0,1,-1,0}; struct node{ int to,cap; int nxt; node(int a,int b):to(a),cap(b){ } node(){ } }b[M<<1]; int head[N],deep[N]; int n,m,S,T,t=1,Ans,Maxflow,Max;//Ans累計滿意度之和 bool p[Mn][Mn]; int read() { int s=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { s=(s<<1)+(s<<3)+c-'0'; c=getchar(); } return s; } void add(int x,int y,int cap) { b[++t]=node(y,cap); b[t].nxt=head[x]; head[x]=t; b[++t]=node(x,0); b[t].nxt=head[y]; head[y]=t; return; } int Cag(int x,int y)//二維轉化為一維 { return (x-1)*m+y; } bool BFS() { int i,cur; int to,cap; queue<int>p; memset(deep,0,sizeof(deep)); deep[S]=1;p.push(S); while(!p.empty()) { cur=p.front();p.pop(); for(i=head[cur];i;i=b[i].nxt) { to=b[i].to;cap=b[i].cap; if(cap&&!deep[to]) { deep[to]=deep[cur]+1; p.push(to); if(to==T) return 1; } } } return 0; } int Dinic(int k,int flow) { if(k==T) return flow; int i,to,cap,res,rest=flow; for(i=head[k];i&&rest;i=b[i].nxt) { to=b[i].to;cap=b[i].cap; if(cap&&deep[to]==deep[k]+1) { res=Dinic(to,min(rest,cap)); if(!res) deep[to]=0; b[i].cap-=res; b[i^1].cap+=res; rest-=res; } } return flow-rest; } int main() { int i,j,k; int to,cap,flow; int Tx,Ty; n=read();m=read(); Max=n*m;T=4*Max+1; for(i=1;i<=n;i++) p[i][0]=p[i][m+1]=1; for(i=1;i<=m;i++) p[0][i]=p[n+1][i]=1; for(i=1;i<=n;i++) for(j=1;j<=m;j++) { Ans+=cap=read(); add(S,Cag(i,j),cap);//選文科 add(Cag(i,j),Cag(i,j)+Max,INF);//自己的兩個結點相連 } for(i=1;i<=n;i++) for(j=1;j<=m;j++) { Ans+=cap=read(); add(Cag(i,j)+Max,T,cap);//選理科 } for(i=1;i<=n;i++) for(j=1;j<=m;j++) { Ans+=cap=read(); to=Cag(i,j)+Max+Max; add(S,to,cap); add(to,Cag(i,j),INF);//別忘了自己 for(k=0;k<4;k++)//組合 { Tx=i+Cx[k];Ty=j+Cy[k]; if(!p[Tx][Ty]) add(to,Cag(Tx,Ty),INF); } } for(i=1;i<=n;i++) for(j=1;j<=m;j++) { Ans+=cap=read(); to=Cag(i,j)+Max+Max+Max; add(to,T,cap); add(Cag(i,j)+Max,to,INF); for(k=0;k<4;k++) { Tx=i+Cx[k];Ty=j+Cy[k]; if(!p[Tx][Ty]) add(Cag(Tx,Ty)+Max,to,INF); } } while(BFS()) while((flow=Dinic(S,INF))) Maxflow+=flow; printf("%d",Ans-Maxflow);//最後還是挺容易的 return 0; }