小Y寫文章 必須匹配和不必須匹配的網路流
題目
https://ac.nowcoder.com/acm/problem/15600
題目描述
小Y寫了一篇文章,他對自己的文筆很有自信,尤其是自己總結出了一套計算文章通順性的公式。
文章共N段,對於文章的每一段小Y對它都能計算出一個估值A_i,而一篇文章的不連貫值定義為\(\max\{|A_i-A_{i-1}|, 2\leq i\leq n\}max{∣A_{i} −A
_{i−1}∣,2≤i≤n}\),現在小Y想要釋出他的文章,但是編輯小Z讓他加入一些廣告,具體來說就是M段估值分別為$B_i的新段落。小Y很頭疼,想讓修改後的文章依然通順,也就是要最小化不連貫值,已知小Y加入新段落的時候不需要考慮新段落之間的順序,但是隻可以在原文章的開頭段之前、結尾段之後、或兩段之間加入一段新段落,每個位置只能加入最多一段。請幫助焦頭爛額的小Y求出將這M個新段落全都加入之後的最小不連貫值。
輸入描述:
多組資料,第一行有一個正整數\(T(T≤30)\)表示資料組數。
之後有T組資料,每組資料第一行有兩個整數\(N,M(1≤N≤200,1≤M≤N+1)。\)
接著有兩行,其中第一行有N個正整數\(A_i\),表示原文章按順序每段的估值。
第二行有M個正整數\(B_i\),表示新段落每段的估值。\((1 \leq A_i, B_i \leq10^9)\)
輸出描述:
對於每組資料,輸出一個整數表示求出的最小不連貫值。
示例1
輸入
2
4 3
1 6 5 2
3 1 4
4 2
1 2 4 3
10 10
輸出
3
7
說明
第一組樣例方案可以是 (1) 1 (4) 6 5 (3) 2
第二組樣例方案可以是 1 2 4 (10) 3 (10)
思路
最小,我們考慮二分判斷滿足。
有n+1個空,有m個元素。那麼就是n個節點和m個節點匹配。
但是有的空是必須匹配。如果a[i+1]-a[i]>mid,那麼這個空就必須填。
有的空是可以點。如果a[i+1]-a[i]<=mid,那麼這個空就可填看不填。
怎麼保證這個必須填的一定填
1:設定一個必須填的次源點,一個可以填的次源點。流相應的流量。那麼要滿流一點要流滿必須流的流量
2:有上下限的網路流
3:費用流,把必須流的節點費用設定1.其他設定為2。
這裡我們用了第1個解決方法。
#pragma GCC optimize(3, "Ofast", "inline") #include<bits/stdc++.h> #define pii pair<int, int> #define INF 1000000007 using namespace std; using namespace std; const int maxn =500+10; struct Edge { int from,to,cap,flow; Edge() {} Edge(int f,int t,int c,int fl):from(f),to(t),cap(c),flow(fl) {} }; struct Dinic { int n,m,s,t; vector<Edge> edges; vector<int> G[maxn]; int cur[maxn]; int d[maxn]; bool vis[maxn]; void init(int n,int s,int t) { this->n=n, this->s=s, this->t=t; edges.clear(); for(int i=0; i<n; i++) G[i].clear(); } void AddEdge(int from,int to,int cap) { //cout<<from<<" "<<to<<"="<<cap<<endl; edges.push_back( Edge(from,to,cap,0) ); edges.push_back( Edge(to,from,0,0) ); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { queue<int> Q; Q.push(s); memset(vis,0,sizeof(vis)); d[s]=0; vis[s]=true; while(!Q.empty()) { int x=Q.front(); Q.pop(); for(int i=0; i<G[x].size(); ++i) { Edge& e=edges[G[x][i]]; if(!vis[e.to] && e.cap>e.flow) { d[e.to]=1+d[x]; vis[e.to]=true; Q.push(e.to); } } } return vis[t]; } int DFS(int x,int a) { if(x==t || a==0) return a; int flow=0,f; for(int& i=cur[x]; i<G[x].size(); ++i) { Edge& e=edges[G[x][i]]; if(d[e.to]==d[x]+1 && (f=DFS(e.to,min(a,e.cap-e.flow) ) )>0) { e.flow +=f; edges[G[x][i]^1].flow -=f; flow +=f; a-=f; if(a==0) break; } } return flow; } int max_flow() { int ans=0; while(BFS()) { memset(cur,0,sizeof(cur)); ans += DFS(s,INF); } return ans; } } DC; int a[205], b[205]; int n, m; int ok(int x) { //cout<<"-----"<<x<<"-----"<<endl; int S=0, T=500, S1=T-1, S2=T-2; DC.init(505, S, T); int cut=0; DC.AddEdge(S2, 1, 1), DC.AddEdge(S2, n+1, 1);//可以放 for(int i=1; i<n; i++){ if(abs(a[i+1]-a[i])>x){//必須放 cut++; DC.AddEdge(S1, i+1, 1); } else{ DC.AddEdge(S2, i+1, 1); } } DC.AddEdge(S, S1, cut); DC.AddEdge(S, S2, m-cut); for(int i=0; i<=n; i++) { for(int k=1; k<=m; k++) { if(i==0) { if(abs(a[i+1]-b[k])<=x) { DC.AddEdge(i+1, n+1+k, 1); } } else if(i==n) { if(abs(a[i]-b[k])<=x){ DC.AddEdge(i+1, n+1+k, 1); } } else { if(abs(a[i]-b[k])<=x&&abs(a[i+1]-b[k])<=x){ DC.AddEdge(i+1, n+1+k, 1); } } } } for(int i=1; i<=m; i++){ DC.AddEdge(n+1+i, 500, 1); } int ans=DC.max_flow(); //cout<<x<<"="<<ans<<endl; return ans==m; } int main() { int t;scanf("%d", &t); while(t--) { scanf("%d%d", &n, &m); int mi=1<<30, mx=0; for(int i=1; i<=n; i++) { scanf("%d", &a[i]); mi=min(mi, a[i]); mx=max(mx, a[i]); } for(int i=1; i<=m; i++) { scanf("%d", &b[i]); mi=min(mi, b[i]); mx=max(mx, b[i]); } int l=0, r=mx-mi, k=0; while(l<=r) { int mid=l+r>>1; if(ok(mid)) { r=mid-1; k=mid; } else { l=mid+1; } } printf("%d\n", k); } return 0; }