小Y写文章 必须匹配和不必须匹配的网络流

题目

https://ac.nowcoder.com/acm/problem/15600

题目描述

小Y写了一篇文章,他对自己的文笔很有自信,尤其是自己总结出了一套计算文章通顺性的公式。
文章共N段,对于文章的每一段小Y对它都能计算出一个估值A_i,而一篇文章的不连贯值定义为max{|AiAi1|,2in}maxAiAi1,2in,现在小Y想要发布他的文章,但是编辑小Z让他加入一些广告,具体来说就是M段估值分别为$B_i的新段落。小Y很头疼,想让修改后的文章依然通顺,也就是要最小化不连贯值,已知小Y加入新段落的时候不需要考虑新段落之间的顺序,但是只可以在原文章的开头段之前、结尾段之后、或两段之间加入一段新段落,每个位置只能加入最多一段。请帮助焦头烂额的小Y求出将这M个新段落全都加入之后的最小不连贯值。

输入描述:

多组数据,第一行有一个正整数T(T30)表示数据组数。
之后有T组数据,每组数据第一行有两个整数N,M(1N200,1MN+1)
接着有两行,其中第一行有N个正整数Ai,表示原文章按顺序每段的估值。
第二行有M个正整数Bi,表示新段落每段的估值。(1Ai,Bi109)
输出描述:
对于每组数据,输出一个整数表示求出的最小不连贯值。

示例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;
}
posted @   liweihang  阅读(137)  评论(0编辑  收藏  举报
编辑推荐:
· 基于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)
Live2D
欢迎阅读『小Y写文章 必须匹配和不必须匹配的网络流』
点击右上角即可分享
微信分享提示