POJ 3680 Intervals (最小费用最大流+离散化)

本文探讨了在有限覆盖次数约束下,最大化覆盖区间总权重的算法实现,通过离散化、构造网络流图并应用最小费用流算法解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Intervals
Time Limit: 5000MS  Memory Limit: 65536K
Total Submissions: 5618  Accepted: 2226

Description

You are given N weighted open intervals. The ith interval covers (ai, bi) and weighs wi. Your task is to pick some of the intervals to maximize the total weights under the limit that no point in the real axis is covered more than k times.

Input

The first line of input is the number of test case.
The first line of each test case contains two integers, N and K (1 ≤ K ≤ N ≤ 200).
The next N line each contain three integers ai, bi, wi(1 ≤ ai < bi ≤ 100,000, 1 ≤ wi ≤ 100,000) describing the intervals.
There is a blank line before each test case.

Output

For each test case output the maximum total weights in a separate line.

Sample Input

4

3 1
1 2 2
2 3 4
3 4 8

3 1
1 3 2
2 3 4
3 4 8

3 1
1 100000 100000
1 2 3
100 200 300

3 2
1 100000 100000
1 150 301
100 200 300

Sample Output

14
12
100000
100301

 首先需要离散化,去掉重复的点。由于区间最多有N=200个,那么最多有400个点。这里离散化的去重操作直接用STL里的unique,二分查找也偷懒地用了upper_bound...

构图:将每个区间s,e连接容量为1,费用为-v的边。假如离散的点有num个,则对于1~num-1,分别连上i->i+1,流量为K,费用为0。再构造源点,连接1,费用为0,流量为K,用num连接汇点,费用也为0,流量为K。然后跑出来的最小费用流的相反数即是答案。

这样构图的大致原因是:源点与离散化后的点1连了流量为K的边,这就限制了每个区间最多只能取K次了。因为,1除了和它的区间右端点连接以外,还跟2连了一条流量为K,费用为0的边。假如1只在一个区间,且右端点为E(设E>K),那么1的流量除了流向E以外,还有K-1可以流向2。若K=1,显然若1流向了E,则不能再流向2了,只就起到1所在区间只能取一次的目的了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define SIZE 512
#define inf 0x3fffffff

using namespace std;

struct node
{
    int to,val,cost,next;
}edge[SIZE*SIZE];

int head[SIZE],idx;
int dis[SIZE],pre[SIZE],pos[SIZE],que[SIZE*SIZE],fr,len;
bool vis[SIZE];
int Case,N,K,sc,sk,pt;
int st[SIZE],ed[SIZE],v[SIZE];
int disc[SIZE],num;

void addnode(int from,int to,int val,int cost)
{
    edge[idx].to = to;
    edge[idx].val = val;
    edge[idx].cost = cost;
    edge[idx].next = head[from];
    head[from] = idx++;
    edge[idx].to = from;
    edge[idx].val = 0;
    edge[idx].cost = -cost;
    edge[idx].next = head[to];
    head[to] = idx++;
}

bool spfa()
{
    fr = len = 0;
    for(int i=0; i<=pt; i++)
    {
        dis[i] = inf;
        pre[i] = pos[i] = -1;
        vis[i] = false;
    }
    dis[sc] = 0;
    pre[sc] = sc;
    vis[sc] = true;
    que[len++] = sc;
    while(fr < len)
    {
        int cur = que[fr++];
        vis[cur] = false;
        for(int i=head[cur]; i!=-1; i=edge[i].next)
        {
            int to = edge[i].to;
            if(edge[i].val > 0 && dis[to] > dis[cur] + edge[i].cost)
            {
                dis[to] = dis[cur] + edge[i].cost;
                pre[to] = cur;
                pos[to] = i;
                if(!vis[to])
                {
                    vis[to] = true;
                    que[len++] = to;
                }
            }
        }
    }
    if(pre[sk] !=-1 && dis[sk] < inf)
        return true;
    return false;
}

int CostFlow()
{
    int flow = 0, cost = 0;
    while(spfa())
    {
        int Min = inf;
        for(int i=sk; i!=sc; i=pre[i])
            Min = min(Min,edge[pos[i]].val);
        flow += Min;
        cost += Min*dis[sk];
        for(int i=sk; i!=sc; i=pre[i])
        {
            edge[pos[i]].val -= Min;
            edge[pos[i]^1].val += Min;
        }
    }
    return cost;
}

int main()
{
    scanf("%d",&Case);
    while(Case--)
    {
        scanf("%d%d",&N,&K);
        num = 0;
        for(int i=1; i<=N; i++)
        {
            scanf("%d%d%d",&st[i],&ed[i],&v[i]);
            disc[++num] = st[i];
            disc[++num] = ed[i];
        }
        sort(disc+1,disc+1+num);
        num = unique(disc+1,disc+1+num)-(disc+1);
        idx = 0;
        memset(head,-1,sizeof(head));
        sc = num+1, sk = num+2, pt = sk+1;
        int s,e;
        for(int i=1; i<=N; i++)
        {
            s = upper_bound(disc+1,disc+1+num,st[i])-(disc+1);
            e = upper_bound(disc+1,disc+1+num,ed[i])-(disc+1);
            addnode(s,e,1,-v[i]);
        }
        for(int i=1; i<num; i++)
            addnode(i,i+1,K,0);
        addnode(sc,1,K,0);
        addnode(num,sk,K,0);
        int ans = CostFlow();
        printf("%d\n",-ans);
    }
    return 0;
}


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值