最长k可重线段集问题【网络流24题】【最大流最小费用+错误点】

  在洛谷,拿到了9分、18分、82分、91分都分别是因为什么呢?在后文中有详细的解释。

题目链接


  刚看到这道题的时候,我第一反应就是一道将每个区间的价值变换的最长k可重区间集问题【网络流24题】【最大流最小费用】这道题,但是,按照这个写法,T了,9分……

  为什么会T呢?我开始去想这个问题,对!存在负环!那么改,我把每个点都去拆点,然后限制流过每个点的流量为K,这样子就可以去保证最大K重叠的问题了,但是WA了,18分…… 

  怎么解决WA的问题呢?我想了下会不会是最后的答案上爆了精度!(又让我想起电子科大的校赛被卡了这题),改了精度,91分!!!震惊了,还真是被卡了精度啊。

  那么还有一组样例过不去啊,怎么办呢?然后开始拼命的改了,我控制输出流,也就是流出的流量一定要恒≤K,然后再去跑最大流的最小费用(边为负边权),如何去考虑这个流过的流量限流呢?看到一种很不错的方法,就是对于每个节点的左右区间(我强制令左端点<右端点),那么,接下去,对于左右端点,我们让他们都扩大到自己的两倍,并且,左端点再加上1,如果原本的左右端点相等的话,就会让左端点>右端点,所以要去改变,但是为什么不是去更改大的端点呢?是有原因的。举个例子,(5, 5)和(5, 7),因为(5, 7)这个点是不会经过"5"的,但是我们得对5限流,所以如果同样是从5出发的话会导致5的流出过小了,所以得改!

  改完之后,82分???还T了两组,什么情况?然后突然发现,自己竟然没有加上那个判断左右端点大小的那个判断,如此就会形成死循环了,改完,A了。呼~


#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define esp 1e-6
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 2e3 + 7, maxE = 3e4 + 7, S = 0;
int N, K, head[maxN], cnt, a[507][2], lsan[maxN], T, diff, num[maxN];
ll val[507];
struct Eddge
{
    int nex, u, v, flow;
    ll cost;
    Eddge(int a=-1, int b=0, int c=0, int d=0, ll f=0):nex(a), u(b), v(c), flow(d), cost(f) {}
}edge[maxE];
inline void addEddge(int u, int v, int flow, ll cost)
{
    edge[cnt] = Eddge(head[u], u, v, flow, cost);
    head[u] = cnt++;
}
inline void _add(int u, int v, int flow, ll cost) { addEddge(u, v, flow, cost); addEddge(v, u, 0, -cost); }
int Flow[maxN], pre[maxN];
ll dist[maxN];
bool inque[maxN];
queue<int> Q;
bool spfa()
{
    memset(pre, -1, sizeof(pre));   memset(dist, INF, sizeof(dist));    memset(inque, false, sizeof(inque));
    while(!Q.empty()) Q.pop();
    Q.push(S);  inque[S] = true;    Flow[S] = INF;  dist[S] = 0;
    while(!Q.empty())
    {
        int u = Q.front();  inque[u] = false;   Q.pop();
        for(int i=head[u], v, f; ~i; i=edge[i].nex)
        {
            v = edge[i].v;  f = edge[i].flow;   ll c = edge[i].cost;
            if(f && dist[v] > dist[u] + c)
            {
                dist[v] = dist[u] + c;
                Flow[v] = min(Flow[u], f);
                pre[v] = i;
                if(!inque[v])
                {
                    inque[v] = true;
                    Q.push(v);
                }
            }
        }
    }
    return pre[T] ^ -1;
}
ll EK()
{
    ll ans = 0;
    while(spfa())
    {
        int now = T, las = pre[now];
        while(now)
        {
            edge[las].flow -= Flow[T];
            edge[las ^ 1].flow += Flow[T];
            now = edge[las].u;
            las = pre[now];
        }
        ans += (ll)dist[T] * Flow[T];
    }
    return -ans;
}
inline void init()
{
    cnt = 0;
    memset(head, -1, sizeof(head)); memset(num, 0, sizeof(num));
}
int main()
{
    scanf("%d%d", &N, &K);
    init();
    for(int i=1, y1, y2; i<=N; i++)
    {
        scanf("%d%d%d%d", &a[i][0], &y1, &a[i][1], &y2);
        val[i] = (int)sqrt( (ll)(a[i][0] - a[i][1]) * (ll)(a[i][0] - a[i][1]) + (ll)(y1 - y2) * (ll)(y1 - y2) + esp);
        if(a[i][1] < a[i][0]) swap(a[i][0], a[i][1]);   //这里是为了保证左右区间的区间是正确的,保证左<右(不过去除了这段也是可以的)
        a[i][0] <<= 1;  a[i][1] <<= 1;  a[i][0] |= 1;   //这里一定要先对左区间的值去"|1"目的是排除了端点的干扰,例如这样子的点(我们只考虑x轴),(2,2),(2,3),这样子的时候,我们必须按照这样的顺序
        if(a[i][1] < a[i][0]) swap(a[i][0], a[i][1]);   //因为我们要的是对流出做出的控流
        lsan[i] = a[i][0];  lsan[i + N] = a[i][1];
    }
    sort(lsan + 1, lsan + (N<<1) + 1);
    diff = (int)(unique(lsan + 1, lsan + (N<<1) + 1) - lsan - 1);
    T = diff + 1;
    _add(S, 1, K, 0);
    for(int i=1; i<=diff; i++) _add(i, i + 1, K, 0);
    for(int i=1, L, R; i<=N; i++)
    {
        L = (int)(lower_bound(lsan + 1, lsan + diff + 1, a[i][0]) - lsan);  R = (int)(lower_bound(lsan + 1, lsan + diff + 1, a[i][1]) - lsan);
        _add(L, R, 1, -val[i]);
    }
    printf("%lld\n", EK());
    return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值