计蒜客 - Fantastic Graph
题目链接:https://nanti.jisuanke.com/t/31447
知识点: 最大流
题目大意:
给定一个二分图,左边有 $N$ 个点,右边有 $M$ 个点,给出 $K$ 条边。问是否能从这 $K$ 条边中找出若干条边使得每个点的度数都在 $[L,R]$ 中。
$1 \le N \le 2000, 0 \le M \le 2000, 0 \le K \le 6000, 0 \le L,R \le 300$
解题思路:
不难想到这是一个无源无汇有容量下界网络的可行流问题。
先建一个小源点 $s$ 和小汇点 $t$,一个大源点 $ss$ 和一个大汇点 $st$。从 $st$ 连一条边到 $ss$,从 $ss$ 连一条边到 $s$,从 $t$ 连一条边到 $st$,容量均为 $INF$。 然后将题目给定的边连进网络图中,容量均为 $1$。
对于 $s$ 连向左边每一点(设为 $u$)的边,需要限制它们的流量下界为 $L$,上界为 $R$,做法是从 $s$ 向 $u$ 连一条容量为 $R-L$ 的边,从 $ss$ 向 $u$ 连一条容量为 $L$ 的边,再从 $s$ 连一条容量为 $L$ 的边到大汇点。如果所有容量为 $L$ 的附加边都满流,则证明有可行流,输出"Yes"。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 const int MAXN = 4010;//点数的最大值 6 const int MAXM = 40010;//边数的最大值 7 const int INF = 0x3f3f3f3f; 8 struct Edge 9 { 10 int to,next,cap,flow; 11 }edge[MAXM];//注意是MAXM 12 int tol; 13 int head[MAXN]; 14 int gap[MAXN],dep[MAXN],cur[MAXN]; 15 void init(){ 16 tol = 0; 17 memset(head,-1,sizeof(head)); 18 } 19 void addedge(int u,int v,int w,int rw = 0){ 20 edge[tol].to = v; edge[tol].cap = w; edge[tol].flow = 0; 21 edge[tol].next = head[u]; head[u] = tol++; 22 edge[tol].to = u; edge[tol].cap = rw; edge[tol].flow = 0; 23 edge[tol].next = head[v]; head[v] = tol++; 24 } 25 int Q[MAXN]; 26 void BFS(int start,int end){ 27 memset(dep,-1,sizeof(dep)); 28 memset(gap,0,sizeof(gap)); 29 gap[0] = 1; 30 int front = 0, rear = 0; 31 dep[end] = 0; 32 Q[rear++] = end; 33 while(front != rear){ 34 int u = Q[front++]; 35 for(int i = head[u]; i != -1; i = edge[i].next){ 36 int v = edge[i].to; 37 if(dep[v] != -1)continue; 38 Q[rear++] = v; 39 dep[v] = dep[u] + 1; 40 gap[dep[v]]++; 41 } 42 } 43 } 44 int S[MAXN]; 45 int sap(int start,int end,int N){ 46 BFS(start,end); 47 memcpy(cur,head,sizeof(head)); 48 int top = 0; 49 int u = start; 50 int ans = 0; 51 while(dep[start] < N){ 52 if(u == end){ 53 int Min = INF; 54 int inser; 55 for(int i = 0;i < top;i++) 56 if(Min > edge[S[i]].cap - edge[S[i]].flow){ 57 Min = edge[S[i]].cap - edge[S[i]].flow; 58 inser = i; 59 } 60 for(int i = 0;i < top;i++){ 61 edge[S[i]].flow += Min; 62 edge[S[i]^1].flow -= Min; 63 } 64 ans += Min; 65 top = inser; 66 u = edge[S[top]^1].to; 67 continue; 68 } 69 bool flag = false; 70 int v; 71 for(int i = cur[u]; i != -1; i = edge[i].next){ 72 v = edge[i].to; 73 if(edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]){ 74 flag = true; 75 cur[u] = i; 76 break; 77 } 78 } 79 if(flag){ 80 S[top++] = cur[u]; 81 u = v; 82 continue; 83 } 84 int Min = N; 85 for(int i = head[u]; i != -1; i = edge[i].next) 86 if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min){ 87 Min = dep[edge[i].to]; 88 cur[u] = i; 89 } 90 gap[dep[u]]--; 91 if(!gap[dep[u]])return ans; 92 dep[u] = Min + 1; 93 gap[dep[u]]++; 94 if(u != start)u = edge[S[--top]^1].to; 95 } 96 return ans; 97 } 98 int rec[MAXM<<1],cnt; 99 int main(){ 100 int N,M,K,kase=1; 101 while(~scanf("%d%d%d",&N,&M,&K)){ 102 int L,R,cnt=0; 103 scanf("%d%d",&L,&R); 104 init(); 105 int s=N+M+1,t=N+M+2,ss=N+M+3,st=N+M+4; 106 addedge(st,ss,INF); 107 for(int i=0;i<K;i++){ 108 int u,v; 109 scanf("%d%d",&u,&v); 110 addedge(u,v+N,1); 111 } 112 for(int i=1;i<=N;i++){ 113 rec[cnt++]=tol; 114 addedge(ss,i,L); 115 rec[cnt++]=tol; 116 addedge(s,st,L); 117 addedge(s,i,R-L); 118 } 119 for(int i=1;i<=M;i++){ 120 rec[cnt++]=tol; 121 addedge(ss,t,L); 122 rec[cnt++]=tol; 123 addedge(i+N,st,L); 124 addedge(i+N,t,R-L); 125 } 126 addedge(ss,s,INF); 127 addedge(t,st,INF); 128 sap(ss,st,N+M+4); 129 LL tot=0; 130 for(int i=0;i<cnt;i++) tot+=edge[rec[i]].flow; 131 printf("Case %d: ",kase++); 132 if(tot==1ll*L*cnt) puts("Yes"); 133 else puts("No"); 134 } 135 136 return 0; 137 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”