SPOJ 2321 Segments(差分约束)

SPOJ Problem Set (classical)

2321. Segments

Problem code: SEGMENTS

There are N horizontal line segments in the plane. The ith segment has some height hi (which may be negative) and runs from x = ai to x = bi (ai < bi). Segments do not contain their endpoints. You must draw a set of vertical lines (note lines and not line segments) so that every given horizontal segment is intersected at least once and at most R times by vertical lines in such a way that R is minimized.

Input

The first line of the input is N (1 ≤ N ≤ 400), the number of horizontal line segments. N lines then follow, where the ith line is "ai bi hi". Each of ai,bi,hi are 32-bit signed integers. Horizontal segments may overlap.

Output

Your output should consist of a single integer, the smallest value of R that is achievable, followed by a newline.

Example

Input:
3
0 1 5
0 2 -2
1 2 7

Output:
2

题意:在二维坐标给定一条水平直线的起点和终点和高度(直线不包括终点),用一些竖直的直线切这些水平直线,每一条直线至少被切一次,问被切最多的直线最少的切割次数

题解:该题可以用差分约束构图,先离散化,然后每个点之间必定有大于等于0条竖直直线,每条线的2个端点之间必定有大于等于1条竖直直线,然后二分枚举每条直线2个端点之间最多切割的直线的次数,若该次数不使图构成负权环,则可以最大切割次数可以少于等于该值,代码转化为求最短路


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
const int N=805;
const int INF=1<<28;
using namespace std;
struct edge{
    long long a,b,h;
}p[N];
struct line{
    int x,y,len;
}v[N<<2];
int n,cou,all,dis[N];
map<long long,int>cc;
void input()
{
    long long c[N<<1];
    cou=0;
    for(int i=0;i<n;i++)
    {
        scanf("%I64d%I64d%I64d",&p[i].a,&p[i].b,&p[i].h);
        p[i].a=p[i].a*2,p[i].b=p[i].b*2-1;
        c[cou++]=p[i].a,c[cou++]=p[i].b;
    }
    sort(c,c+cou);
    cou=unique(c,c+cou)-c;
    for(int i=0;i<cou;i++) cc[c[i]]=i;
}
void add(int x,int y,int len)
{
    v[all].x=x;
    v[all].y=y;
    v[all++].len=len;
}
void build(int len)
{
    all=0;
    for(int i=0;i<n;i++)
    {
        int x=cc[p[i].a],y=cc[p[i].b];
        add(y,x,-1),add(x,y,len);
    }
    for(int i=1;i<cou;i++) add(i,i-1,0);
}
int bellman()
{
    for(int i=1;i<=cou;i++) dis[i]=INF;
    dis[0]=0;
    for(int i=0;i<cou;i++)
    {
        int flag=1;
        for(int j=0;j<all;j++)
        {
            if(dis[v[j].x]+v[j].len<dis[v[j].y])
                dis[v[j].y]=dis[v[j].x]+v[j].len,flag=0;
        }
        if(flag) return 1;
    }

    return 0;
}
int run()
{
    int res=n,l=1,r=n,mid;

    while(l<=r)
    {
        mid=(l+r)>>1;
        build(mid);
        if(bellman())
        {
            r=mid-1;
            res=mid;
        }
        else l=mid+1;
    }

    return res;
}
int main()
{
    //freopen("t.txt","r",stdin);
    while(scanf("%d",&n)>0)
    {
        input();
        printf("%d\n",run());
    }

    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值