標籤:bzoj2127 happiness 最小割
轉載請註明出處:http://blog.csdn.net/vmurder/article/details/42609669
其實我就是覺得原創的訪問量比未授權盜版多有點不爽233。。。
那個一看就覺得不是費用流就是最小割。
想想就確定最小割了。
考慮到一個人,文理不可兼得,不妨先建點,然後向源點(文科),匯點(理科)連邊,流量(也就是割)是對應喜悅值。(這裡的想法是先建個差不多的,有漏洞再拆點啊,建輔助點啊什麼的)
然後再考慮一對朋友之間的共文理喜悅值:
如果都選文,那麼需要割掉雙方都選理的喜悅值,
如果都選理,那麼需要割掉雙方都選文的喜悅值,
如果一文一理,那麼就都割。
首先對於一個點對,我們考慮到他們之間的關係只在雩都選文或者都選理的喜悅值,
而求最小割時我們要割掉這部分權值,那麼我們可以建邊,然後把邊進行處理。
一、
首先我最開始的想法是把邊的權值設定為都選文+都選理的和。
這樣就成功地滿足了一文一理的情況。
但是卻無法分開。
二、
這時候我們可以把邊建成點,然後兩邊的人向此點連容量inf的無向邊,
然後源點(文科)向此點連都選文的容量,匯點連都選理的。
這樣我們就滿足了都選文或者都選理的情況。
也就是都選文的話,我們就可以不割“都選文”這種權值,都選理 同理。
但是這是WA的,cheat也不行(實測),因為我們又無法讓兩人一文一理了。
三、
再往“一”那裡考慮,發現我們有時需要把兩種權值都割斷。
這個時候想到:
a-(len)-->b,當len值一定,把圖建成a-->b-->c-->d,只要邊長都是len,那麼最小割是不變的。
所以我們把“邊點”拆成兩個點,一個對應S集(文),一個對應T集(理),,
這樣我們就可以把原來的inf也改成對應權值,使得割斷哪一條邊都是最小割值。
這麼說可能有些含糊,不妨來張圖。
這張圖就是建法,不妨簡單分析一下就可以理解為什麼了。
貼代碼:
#include <queue>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 50005#define M 300000#define P 105#define inf 0x3f3f3f3fusing namespace std;struct KSD{int v,len,next;}e[M];int head[N],cnt;inline void add(int u,int v,int len){e[++cnt].v=v;e[cnt].len=len;e[cnt].next=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].len=len;e[cnt].next=head[v];head[v]=cnt;}int s,t,d[N];queue<int>q;bool bfs(){while(!q.empty())q.pop();memset(d,0,sizeof(d));int i,u,v;q.push(s),d[s]=1;while(!q.empty()){u=q.front(),q.pop();for(i=head[u];i;i=e[i].next){v=e[i].v;if(!d[v]&&e[i].len){d[v]=d[u]+1;if(v==t)return 1;q.push(v);}}}return 0;}int dinic(int x,int flow){if(x==t)return flow;int remain=flow,i,v,k;for(i=head[x];i&&remain;i=e[i].next){v=e[i].v;if(d[v]==d[x]+1&&e[i].len){k=dinic(v,min(remain,e[i].len));if(!k)d[v]=0;e[i].len-=k,e[i^1].len+=k;remain-=k;}}return flow-remain;}int n,m,id[P][P];int sum,maxflow;void build(){int i,j,a,temp;scanf("%d%d",&n,&m),temp=n*m;s=n*m+2*(n-1)*m+2*n*(m-1)+1,t=n*m+2*(n-1)*m+2*n*(m-1)+2;for(i=1;i<=n;i++)for(j=1;j<=m;j++)id[i][j]=++cnt;cnt=1;// 第一個矩陣為n行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學選擇文科獲得的喜悅值。for(i=1;i<=n;i++)for(j=1;j<=m;j++){scanf("%d",&a);sum+=a;add(s,id[i][j],a);}// 第二個矩陣為n行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學選擇理科獲得的喜悅值。for(i=1;i<=n;i++)for(j=1;j<=m;j++){scanf("%d",&a);sum+=a;add(id[i][j],t,a);}// 第三個矩陣為n-1行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i+1行第j列的同學同時選擇文科獲得的額外喜悅值。for(i=1;i<n;i++)for(j=1;j<=m;j++){scanf("%d",&a);sum+=a,++temp;add(id[i][j],temp,a),add(id[i+1][j],temp,a);add(s,temp,a);}// 第四個矩陣為n-1行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i+1行第j列的同學同時選擇理科獲得的額外喜悅值。for(i=1;i<n;i++)for(j=1;j<=m;j++){scanf("%d",&a);sum+=a,++temp;add(id[i][j],temp,a),add(id[i+1][j],temp,a);add(temp,t,a);}// 第五個矩陣為n行m-1列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i行第j+1列的同學同時選擇文科獲得的額外喜悅值。for(i=1;i<=n;i++)for(j=1;j<m;j++){scanf("%d",&a);sum+=a,++temp;add(id[i][j],temp,a),add(id[i][j+1],temp,a);add(s,temp,a);}// 第六個矩陣為n行m-1列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i行第j+1列的同學同時選擇理科獲得的額外喜悅值。for(i=1;i<=n;i++)for(j=1;j<m;j++){scanf("%d",&a);sum+=a,++temp;add(id[i][j],temp,a),add(id[i][j+1],temp,a);add(temp,t,a);}}int main(){//freopen("test.in","r",stdin);build();while(bfs())maxflow+=dinic(s,inf);printf("%d\n",sum-maxflow);return 0;}
【BZOJ2127】happiness 最小割 自己YY出來的建圖、