Gym - 101986H Homework (最大流和最小割)
阿新 • • 發佈:2018-11-21
題意:有N個作業,分別為A類和B類,每天最多隻能選擇AB中的一類做,如果該天有該類的作業,就必須要做一個,問最多能做多少作業,和最少要做多少作業。
解題思路:對於最多能做多少,很容易建圖
對於每一天建一個點
對於每一個作業建一個點
每個作業覆蓋的天都跟該作業連邊,流量為1
源點跟每一天連邊,流量為1
每一個作業跟匯點連邊,流量為1.
最大流即最多能做的作業。
其實就是個最大匹配過程。
最小怎麼求呢?最小容易聯想到最小割。因此把模型轉化為最小割模型,每割一條邊,代表做了那個作業,那麼我們只要求出最小割,即求出了最小要做的作業。但是這裡還有天數的限制,即每一天必須要做一個作業,除非沒有該類作業。那麼我們只要把天的限制也考慮進去即可。具體建圖如下
把每一天拆點,變成800個點。起點與終點之間連邊,流量為1
對於每一個A類作業,把A類作業覆蓋的天,向該天的起點連邊,流量為1.
對於每一個B類作業,把B類作業覆蓋的天,向該天的終點連邊,流量為1.
源點跟A類每一個點連邊
B類每一個點向匯點連邊
最後最小割即為所求。
#include<bits/stdc++.h> using namespace std; const int MAXN=1605; const int INF=0x3f3f3f3f; typedef long long ll; int N,M; int cnt=0; struct edge{ int v,next,flow; }e[800*800*2]; int head[MAXN],edge_num,layer[MAXN]; void insert_edge(int u,int v,int w){ e[edge_num].v=v; e[edge_num].flow=w; e[edge_num].next=head[u]; head[u]=edge_num++; e[edge_num].v=u; e[edge_num].flow=0; e[edge_num].next=head[v]; head[v]=edge_num++; } bool bfs(int start,int End){ queue<int> Q; Q.push(start); memset(layer,0,sizeof(layer)); layer[start]=1; while (Q.size()) { int u=Q.front(); Q.pop(); if(u==End) return true; for(int j=head[u];~j;j=e[j].next){ int v=e[j].v; if(layer[v]==false&&e[j].flow){ layer[v]=layer[u]+1; Q.push(v); } } } return false; } int dfs(int u,int Maxflow,int End){ if(u==End) return Maxflow; int uflow=0; for(int j=head[u];~j;j=e[j].next){ int v=e[j].v; if(layer[v]-1==layer[u]&&e[j].flow){ int flow=min(Maxflow-uflow,e[j].flow); flow=dfs(v,flow,End); e[j].flow-=flow; e[j^1].flow+=flow; uflow+=flow; if(uflow==Maxflow) break; } } if(uflow==0) layer[u]=0; return uflow; } int dinic(int start,int End){ int Maxflow=0; while(bfs(start,End)) Maxflow+=dfs(start,INF,End); return Maxflow; } struct point{ int s,e; }P[MAXN*4]; int in[MAXN],out[MAXN],tian1[MAXN],tian2[MAXN]; int main(){ scanf("%d%d",&N,&M); for(int i=1;i<=N;i++) scanf("%d%d",&P[i].s,&P[i].e); memset(head,-1,sizeof(head)); edge_num=0; cnt=0; int S,T; S=++cnt; for(int i=1;i<=400;i++) in[i]=++cnt; for(int i=1;i<=N;i++) out[i]=++cnt; T=++cnt; for(int i=1;i<=N;i++) for(int j=P[i].s;j<=P[i].e;j++) insert_edge(in[j],out[i],INF); for(int i=1;i<=400;i++) insert_edge(S,in[i],1); for(int i=1;i<=N;i++) insert_edge(out[i],T,1); int ans1=dinic(S,T); memset(head,-1,sizeof(head)); edge_num=0; cnt=0; S=++cnt; for(int i=1;i<=M;i++) in[i]=++cnt; for(int i=M+1;i<=N;i++) out[i]=++cnt; for(int i=1;i<=400;i++) tian1[i]=++cnt; for(int i=1;i<=400;i++) tian2[i]=++cnt; T=++cnt; for(int i=1;i<=M;i++) insert_edge(S,in[i],1); for(int i=1;i<=M;i++) for(int j=P[i].s;j<=P[i].e;j++) insert_edge(in[i],tian1[j],1); for(int i=1;i<=400;i++) insert_edge(tian1[i],tian2[i],1); for(int i=M+1;i<=N;i++) for(int j=P[i].s;j<=P[i].e;j++) insert_edge(tian2[j],out[i],1); for(int i=M+1;i<=N;i++) insert_edge(out[i],T,1); int ans2=dinic(S,T); printf("%d\n%d",ans1,ans2); return 0; }