小Y写文章 必须匹配和不必须匹配的网络流
题目
https://ac.nowcoder.com/acm/problem/15600
题目描述
小Y写了一篇文章,他对自己的文笔很有自信,尤其是自己总结出了一套计算文章通顺性的公式。
文章共N段,对于文章的每一段小Y对它都能计算出一个估值A_i,而一篇文章的不连贯值定义为,现在小Y想要发布他的文章,但是编辑小Z让他加入一些广告,具体来说就是M段估值分别为$B_i的新段落。小Y很头疼,想让修改后的文章依然通顺,也就是要最小化不连贯值,已知小Y加入新段落的时候不需要考虑新段落之间的顺序,但是只可以在原文章的开头段之前、结尾段之后、或两段之间加入一段新段落,每个位置只能加入最多一段。请帮助焦头烂额的小Y求出将这M个新段落全都加入之后的最小不连贯值。
输入描述:
多组数据,第一行有一个正整数表示数据组数。
之后有T组数据,每组数据第一行有两个整数
接着有两行,其中第一行有N个正整数,表示原文章按顺序每段的估值。
第二行有M个正整数,表示新段落每段的估值。
输出描述:
对于每组数据,输出一个整数表示求出的最小不连贯值。
示例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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)