埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛 J 小Y写文章 [二分+费用流]

本文介绍了一种结合二分查找与费用流算法解决特定数组插入问题的方法。问题要求在保持数组特性的同时,通过插入另一数组的元素来最小化数组的价值,并确保所有待插入元素都被正确放置。

题意:给你数组A,定义value为:


再给一个数组B,数组B的每个数都可以插入数组A的相邻两个数之间(首尾也能插入),但是A数组的相邻两个数之间只能插入一个Bi,且B数组的所有数,都必须插入,求插入B数组后A数组value的最小值。

题解:我们二分答案,对于当前mid表示最大值小于等于mid的时候是否存在可行解,因此有两种情况:

①A数组本身就存在|Ai-Ai-1|>mid。

②B数组插入要满足|Bj-Ai|<=mid同时|Bj-Ai-1|<=mid。

于是我们可以通过费用流建立匹配的图,将源点连接所有的B,汇点连接所有的A,对于|Ai-Ai-1|>mid情况,我们找B数组中能插入之后满足条件的点Bj,将Bj与Ai相连,流量为1费用为0,对于|Ai-Ai-1|<=mid的情况,我们将可以插入的Bj,连接Bj与Ai流量为1,费用为1,。这样建边后,会优先流入必须插点的位置,最后只要判断m-cost是否等于必须要查点的位置的个数,并且流量是否为m就可以了判断是否有可行解了。

AC代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<queue>
using namespace std;
int n,m;
int a[205],b[205];
namespace MCMF {
	int INF=1<<29;
	const int MX=605;
    int S, T;//源点,汇点
    int erear, n;
    int st, en, maxflow, mincost;
    bool vis[MX];
    int Head[MX], cur[MX], dis[MX];
    const int ME = 4e5 + 5;//边的数量

    queue <int> Q;
    struct Edge {
        int v, cap, cost, nxt, flow;
        Edge() {}
        Edge(int a, int b, int c, int d) {
            v = a, cap = b, cost = c, nxt = d, flow = 0;
        }
    } E[ME], SE[ME];

    void init(int _n) {
        n = _n, erear = 0;
        for(int i = 0; i <= n; i++) Head[i] = -1;
    }
    void addedge(int u, int v, int cap, int cost) {
        E[erear] = Edge(v, cap, cost, Head[u]);
        Head[u] = erear++;
        E[erear] = Edge(u, 0, -cost, Head[v]);
        Head[v] = erear++;
    }
    bool adjust() {
        int v, min = INF;
        for(int i = 0; i <= n; i++) {
            if(!vis[i]) continue;
            for(int j = Head[i]; ~j; j = E[j].nxt) {
                v = E[j].v;
                if(E[j].cap - E[j].flow) {
                    if(!vis[v] && dis[v] - dis[i] + E[j].cost < min) {
                        min = dis[v] - dis[i] + E[j].cost;
                    }
                }
            }
        }
        if(min == INF) return false;
        for(int i = 0; i <= n; i++) {
            if(vis[i]) {
                cur[i] = Head[i];
                vis[i] = false;
                dis[i] += min;
            }
        }
        return true;
    }
    int augment(int i, int flow) {
        if(i == en) {
			mincost += dis[st] * flow;
            maxflow += flow;
            return flow;
        }
        vis[i] = true;
        for(int j = cur[i]; j != -1; j = E[j].nxt) {
            int v = E[j].v;
            if(E[j].cap == E[j].flow) continue;
            if(vis[v] || dis[v] + E[j].cost != dis[i]) continue;
            int delta = augment(v, std::min(flow, E[j].cap - E[j].flow));
            if(delta) {
                E[j].flow += delta;
                E[j ^ 1].flow -= delta;
                cur[i] = j;
                return delta;
            }
        }
        return 0;
    }
    void spfa() {
        int u, v;
        for(int i = 0; i <= n; i++) {
            vis[i] = false;
            dis[i] = INF;
        }
        Q.push(st);
        dis[st] = 0; vis[st] = true;
        while(!Q.empty()) {
            u = Q.front(), Q.pop(); vis[u] = false;
            for(int i = Head[u]; ~i; i = E[i].nxt) {
                v = E[i].v;
                if(E[i].cap == E[i].flow || dis[v] <= dis[u] + E[i].cost) continue;
                dis[v] = dis[u] + E[i].cost;
                if(!vis[v]) {
                    vis[v] = true;
                    Q.push(v);
                }
            }
        }
        for(int i = 0; i <= n; i++) {
            dis[i] = dis[en] - dis[i];
        }
    }
    int zkw(int s, int t, int &ret_flow) {
        st = s, en = t;
        spfa();
        mincost = maxflow = 0;
        for(int i = 0; i <= n; i++) {
            vis[i] = false;
            cur[i] = Head[i];
        }
        do {
            while(augment(st, INF)) {
                memset(vis, false, n * sizeof(bool));
            }
        } while(adjust());
        ret_flow = maxflow;
        return mincost;
    }
    void out(int pp){
    	for(int i=0;i<pp;i+=2){
	    	if(E[i].flow==E[i].cap)printf("%d ",i/2+1);
	    }
	    printf("\n");
    }
}
int judge(int num)
{
	MCMF::init(n+m+6);
	for(int i=1;i<=m;i++)
        MCMF::addedge(0,i,1,0);
	for(int i=1;i<=n+1;i++)
        MCMF::addedge(i+m,n+m+3,1,0);
    int need=0;
    for(int i=1;i<=n+1;i++)
    {
        if(abs(a[i]-a[i-1])>num)need++;
        for(int j=1;j<=m;j++)
            if(abs(b[j]-a[i-1])<=num&&abs(b[j]-a[i])<=num)
            {
                if(abs(a[i]-a[i-1])<=num)MCMF::addedge(j,i+m,1,1);
                else MCMF::addedge(j,i+m,1,0);
            }
    }
    int flow;
    int cost=MCMF::zkw(0,n+m+3,flow);
    return (flow==m&&m-cost==need);
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		a[n+1]=a[n];
		a[0]=a[1];
		for(int i=1;i<=m;i++)
			scanf("%d",&b[i]);
		int l=0,r=1000000005;
		while(l<=r)
		{
			int mid=l+r>>1;
			if(judge(mid))r=mid-1;
			else l=mid+1;
		}
		printf("%d\n",l);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值