poj 1436 Horizontally&…


http://poj.org/problem?id=1436

题目大意:
1.给出n条垂直x轴的线段。
2.定义两条线段水平可见当且仅当存在一条平行于x轴的线段,它的两个端点落在这两条线段上并且不与第三条线段相交。
3.定义三条线段构成一个三角形当且仅当这三条线短两两之间水平可见。
求这n条线段可以构成多少个三角形。
//====================================================================
思路:
非常自然的想法就是求出所有n条线段之间水平可见的关系然后用SPFA之类的做法求长度为3的环的个数。

求线段之间水平可见关系可以利用线段树做:按x坐标排序,覆盖的y1-y2之间所有的点。如果查询和覆盖同时进行,覆盖的判断和平时不太相同:当前区间被完全包含的时候不能直接覆盖就返回,而是要先往下找了以后返回了再覆盖。
这里有个很妙的trick,对于以下数据:
1 4 1
1 2 2
3 4 3
1 4 4
以上的做法会判断第一条和第四条不水平可见。但只要 在覆盖的时候把y1和y2乘以2就能解决这个问题了(但是空间花费必然变大了)。

对于判断三元环的算法?直接暴力吧。。
想象每一个线段是一个抽象的点,两条线段水平可见则根据定义连一条不与第三条线段相交的线段。这样构图以后会发现这是一个平面图。而平面图有一个性质: 边数<=(点数-2)*3

可以这么想:已经存在一个平面图了,并且这个平面图中边数已经达到最大,此时再加入一个点,如果这个点可以和四个以上包括四个的点相连并且满足平面图的性质。那么之前的那个平面图的边数就不是最大化的,因为这四个以上的点之间肯定还能再连一条以上的边。所以每加入一个点边数增加3,初始的时候三个点三条边构成三角形。

这样说不是很严密,但是应该是没有错的吧。。求指教。

嗯,这题还学习了容器vector,之前用得极少,而且这种东西不用马上就丢了。以后每次新学的东西都要整理起来、
vector:阿德

Code:


#include
#include
#include
#include
#include
#include
using namespace std;
struct Line
{
    int x, y1, y2;
    Line()
    {

    }
    Line(int X, int Y1, int Y2) : x(X), y1(Y1), y2(Y2)
    {

    }
} l[8010];
vector v[8010];
int a[70000];
int s, t, z, n;
int cmp(Line g, Line h)
{
    return g.x < h.x;
}
void cover(int be, int en, int now)
{
    if (s <= be && en <= t && a[now] > -1)         //多加了a[now]>-1的判断为了继续往下找
    {
        if (a[now])
            v[z].push_back(a[now]);         //这里只记录有向边在统计答案的时候会方便些
        a[now] = z;
        return;
    }
    int g = now * 2, h = g + 1;
    if (a[now] > 0)         //往下推标记
        a[g] = a[now],
        a[h] = a[now];
    int mid = (be + en) / 2;
    if (s <= mid)
        cover(be, mid, g);
    if (t > mid)
        cover(mid + 1, en, h);
    if (a[g] == a[h])         //-1表示这一段有多条覆盖,0表示没有覆盖,数字表示完全被某段覆盖
        a[now] = a[g];
    else
        a[now] = -1;
}
int main()
{
    freopen("1.in", "r", stdin);
    int ttt;
    scanf("%d", &ttt);
    while (ttt--)
    {

//==================================================
        memset(a, 0, sizeof(a));
//==================================================

        scanf("%d", &n);
        int ymin = 9999;
        int ymax = -ymin;
        for (int i = 1; i <= n; i++)
        {
            int x, y1, y2;
            scanf("%d%d%d", &y1, &y2, &x);
            l[i] = Line(x, y1, y2);
            ymin = min(ymin, y1);
            ymax = max(ymax, y2);
        }
        sort(l + 1, l + n + 1, cmp);
        ymax -= ymin;
        ymax *= 2;
        for (int i = 1; i <= n; i++)
        {
            v[i].clear();         // 记得要每次清空容器。。。。
            l[i].y1 -= ymin;
            l[i].y2 -= ymin;
            s = l[i].y1 * 2;
            t = l[i].y2 * 2;
            z = i;
            cover(0, ymax, 1);
        }
        for (int i = 1; i <= n; i++)
        {
            sort(v[i].begin(), v[i].end());
            vector::iterator iter = unique(v[i].begin(), v[i].end());
            v[i].erase(iter, v[i].end());
//             printf("%d:%d %d %d\n", i, l[i].x, l[i].y1, l[i].y2);
//             for (iter = v[i].begin(); iter != v[i].end(); iter++)
//                 printf("%d ", *iter);
//             printf("\n");
        }
        int ans = 0;
        for (int i = 1; i <= n; i++)         //这里每个点只会记录之前的边中有哪些水平可见
            for (int j = 0; j < v[i].size(); j++)
                for (int k = 0; k < v[v[i][j]].size(); k++)
                    for (int t = 0; t < v[i].size(); t++)
                        if (v[i][t] == v[v[i][j]][k])
                            ans++;
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值