小Y写文章

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述 

小Y写了一篇文章,他对自己的文笔很有自信,尤其是自己总结出了一套计算文章通顺性的公式。

文章共N段,对于文章的每一段小Y对它都能计算出一个估值,而一篇文章的不连贯值定义为,现在小Y想要发布他的文章,但是编辑小Z让他加入一些广告,具体来说就是M段估值分别为的新段落。小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个物品去填充。如果某个物品可以放在某个位置,就连边,容量为 1。需要额外增加一个节点,如果某个位置可以不放物品,这个节点就和那个位置连边。看看网络最大流是不是为n+1即可。如果是,则说明存在放置的方案


#include <iostream> 
#include <cstdio> 
#include <cstring> 
#include <string> 
#include <algorithm> 
#include <cmath>
#include <map> 
#include <set>
#include <stack>
#include <queue> 
#include <vector> 
#include <bitset> 
#include <functional> 
 
using namespace std;
 
#define LL long long
const int INF = 0x3f3f3f3f;
#define MAXN 500   
 
struct node
{
    int u, v, next, cap;
} edge[MAXN * MAXN];
int nt[MAXN], s[MAXN], d[MAXN], visit[MAXN];
int a[MAXN], b[MAXN];
int cnt, n, m;
 
void init()
{
    cnt = 0;
    memset(s, -1, sizeof s);
}
 
void add(int u, int v, int c)
{
    edge[cnt].u = u;
    edge[cnt].v = v;
    edge[cnt].cap = c;
    edge[cnt].next = s[u];
    s[u] = cnt++;
    edge[cnt].u = v;
    edge[cnt].v = u;
    edge[cnt].cap = 0;
    edge[cnt].next = s[v];
    s[v] = cnt++;
}
 
bool BFS(int ss, int ee)
{
    memset(d, 0, sizeof d);
    d[ss] = 1;
    queue<int>q;
    q.push(ss);
    while (!q.empty())
    {
        int pre = q.front();
        q.pop();
        for (int i = s[pre]; ~i; i = edge[i].next)
        {
            int v = edge[i].v;
            if (edge[i].cap > 0 && !d[v])
            {
                d[v] = d[pre] + 1;
                q.push(v);
            }
        }
    }
    return d[ee];
}
 
int DFS(int x, int exp, int ee)
{
    if (x == ee || !exp) return exp;
    int temp, flow = 0;
    for (int i = nt[x]; ~i; i = edge[i].next, nt[x] = i)
    {
        int v = edge[i].v;
        if (d[v] == d[x] + 1 && (temp = (DFS(v, min(exp, edge[i].cap), ee))) > 0)
        {
            edge[i].cap -= temp;
            edge[i ^ 1].cap += temp;
            flow += temp;
            exp -= temp;
            if (!exp) break;
        }
    }
    if (!flow) d[x] = 0;
    return flow;
}
 
int Dinic_flow(int ss, int ee)
{
    int ans = 0;
    while (BFS(ss, ee))
    {
        for (int i = 0; i <= n + m + 3; i++) nt[i] = s[i];
        ans += DFS(ss, INF, ee);
    }
    return ans;
}
 
int check(int x)
{
    init();
    add(n + m + 3, 0 + 1 + m, 1);
    add(n + m + 3, n + 1 + m, 1);
    for (int i = 1; i < n; i++)
        if (abs(a[i] - a[i + 1]) <= x)
            add(n + m + 3, i + 1 + m, 1);
    add(0, n + m + 3, n + 1 - m);
    for (int i = 1; i <= m; i++) add(0, i, 1);
    for (int i = 1; i <= m; i++)
    {
        for (int j = 0; j <= n; j++)
        {
            if (j == 0)
            {
                if (abs(b[i] - a[1]) <= x)
                    add(i, j + 1 + m, 1);
            }
            else if (j < n)
            {
                if (abs(b[i] - a[j]) <= x && abs(b[i] - a[j + 1]) <= x)
                    add(i, j + 1 + m, 1);
            }
            else
            {
                if (abs(b[i] - a[n]) <= x)
                    add(i, j + 1 + m, 1);
            }
        }
    }
    for (int j = 0; j <= n; j++) add(j + 1 + m, n + m + 2, 1);
    if (Dinic_flow(0, n + m + 2) == n + 1) return 1;
    return 0;
}
 
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]);
        for (int i = 1; i <= m; i++) scanf("%d", &b[i]);
        int l = 0, r = 1e9, ans;
        while (l <= r)
        {
            int mid = (l + r) >> 1;
            if (check(mid)) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值