【问题标题】:Beautiful People - Dynamic programming美丽的人 - 动态规划
【发布时间】:2014-05-23 08:43:07
【问题描述】:

我正在尝试在http://acm.sgu.ru/problem.php?contest=0&problem=199 解决美丽的人问题,但在某些测试用例中我得到了错误的答案。

一个城市最负盛名的体育俱乐部正好有 N 个成员。 它的每个成员都强壮而美丽。更准确地说,第 i 个 该俱乐部的成员(成员按他们进入的时间编号 俱乐部)有实力斯和美人碧。由于这是一个非常 有声望的俱乐部,其成员非常富有,因此 非凡的人,所以他们常常极度憎恨对方。 严格来说,俱乐部的第 i 个成员 X 先生讨厌俱乐部的第 j 个成员 如果 Si ≤ Sj 且 Bi ≥ Bj 或如果 Si ≥ Sj 且 Bi ≤ Bj(如果 X先生的两个属性都大于相应的属性 Y 先生,另一方面,如果他的两个 属性少,他很尊重Y先生)。

为了庆祝新的 2003 年,俱乐部的管理部门是 计划组织一个聚会。但是他们害怕如果两个 互相憎恨的人会同时参加聚会,之后 一两杯他们就会开始打架。所以没有两个讨厌的人 应该互相邀请。另一方面,为了保住俱乐部 presti≥在适当的级别,行政部门希望邀请作为 尽可能多的人。

做行政中唯一不怕碰的人 一台计算机,你要编写一个程序来找出谁 邀请参加聚会。

输入

输入文件的第一行包含整数 N — 的数量 俱乐部成员。 (2 ≤ N ≤ 100,000)。接下来的 N 行包含两个 每个数字 - 分别为 Si 和 Bi ( 1 ≤ Si, Bi ≤ 10^9 )。

输出

在输出文件的第一行打印最大数量 可以被邀请参加聚会的人。在第二行输出 N integers — 以任意顺序邀请的成员数量。如果 存在多种解决方案,输出任何一种。

Sample test(s)

Input


4
1 1
1 2
2 1
2 2

Output


2
1 4 

基本上我的做法是:

  1. 首先根据 Beauty[] 对数组 Strength[] 进行排序
  2. 采用一个数组 D[i] 存储最大 lis 直到 i
  3. 最佳解决方案 = D(i) = { 1 + Max ( D(j) ) } 其中 j < iD[i] = max{ D[j] +1 }for j < iStrength[j] < Strength[i]Beauty[j] < Beauty[i] ,如果没有这样的 j 则 D(i) = 1

我的方法有什么遗漏吗?

我的解决方案:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std; 
typedef struct {
    long int s;
    long int b;
} c_type;
int compare(const c_type &a, 
        const c_type &b) {
    return a.s < b.s;
}

int main( )
{


int n = 0;
cin>>n;
vector<c_type> ct;
ct.resize(n);
//vector<long int> b(n,-1);
//s[0] = 1;
//s[1] = 1;
//s[2] = 2;
//s[3] = 2;
//b[0] = 1;
//b[1] = 2;
//b[2] = 1;
//b[3] = 2;

vector<long int> d(n,1);
vector<long int> p(n,-1);
long int max = -1;
long int bestEnd = -1;
for(int i = 0 ;i<n;i++)
{
    cin>>ct[i].s>>ct[i].b;
}
 sort (ct.begin(), ct.end(), compare);
for(int i  = 1 ; i < n ;i++)
{
    for(int j  = i-1 ; j>=0 ; j--)
    {
        if(((d[j] + 1) > d[i]) and (ct[j].b < ct[i].b) and (ct[j].s < ct[i].s))
        {
            d[i] = d[j]+1;
            p[i] = j;
        }
    }
    if(max < d[i])
    {
        max = d[i];
        bestEnd = i;
    }
}
cout<<max<<endl;
if(bestEnd != -1)
while(bestEnd not_eq -1)
{
    cout<<bestEnd+1<<" ";
    bestEnd = p[bestEnd];
}
return 0;
}

【问题讨论】:

  • 这是LIS(最长递增子序列),您的解决方案似乎还可以。假设您有实施错误? (您可以在此处发布您的解决方案)。
  • @juver 我已经更新了我的解决方案。
  • 您的解决方案输出的人员索引不正确。对输入进行排序后,它们的顺序发生了变化。只需将附加索引存储到您的结构中并使用它。但是,您的代码将 TLE,因为 O(N^2)。您需要 O(NlogN) 解决方案,这是 LIS 的经典解决方案。
  • @juver 您应该将此作为答案发布。对于 OP,除了 juver 所说的,如果可以邀请的最大可能成员数量为 1,您也不会正确输出。
  • 这个俱乐部的每个人都讨厌自己 ;-) (因此,可以在不爆发战斗的情况下被邀请参加派对的最大人数是 0。Rockin'。 )

标签: c++ algorithm dynamic-programming


【解决方案1】:

您的解决方案为人员输出了不正确的索引。对输入进行排序后,它们的顺序发生了变化。只需将附加索引存储到您的结构中并使用它。
但是,由于 O(N^2),您的代码将获得 TLE。您需要 O(NlogN) 解决方案,这是 LIS 的经典解决方案。

【讨论】:

  • TLE 和 LIS 是什么意思?
  • LIS - 最长递增子序列,TLE - 在在线评委的情况下,超过时间限制
【解决方案2】:

因此,有一种有趣的方式可以从几何角度考虑这个问题。想象一下,绘制一个以 S,B 为轴的图形,并为每个人放置一个 x。 (也删除任何重复的点,即如果 s=s 和 b=b 你可以只删除其中一个)。

那么对于每个点/人,只有矩形中较低和左侧的点是可行的选择。所以我可以给每个人一个代表尊重他们的人数的数字。拨打这个号码 C。

这为 a* 搜索提供了一种可接受的启发式方法。从右上角的根节点开始,我的“最佳”移动通常是具有最高 C 数的移动,因为它为以后保留了最多的选项。此外,一旦我找到了树根的一个根,我只需要选择其他分支,如果它们的 C 数高于实际人数,那么大部分树会很快终止。

我怀疑这种类型的搜索平均而言是最优的,但根据点的分布情况,它可能会出现缓慢的边缘情况。

所以这演示了它是如何工作的,你从某个根节点开始,在第一次运行时它会到达最高的 C 编号,当它终止时,它会重新计数以提供“答案”,因此它只必须检查 C 数严格大于当前最佳答案的分支。在这种情况下,没有这样的其他分支。

很容易直观地看出,如果点均匀分布在 S 和 B 中,这可能会非常快,但如果它们在 y=x 附近强烈聚集,则可能会非常慢,因为这样您就不会很快排除分支通常。

【讨论】:

  • 它具有完整的 O(NlogN) 解决方案,如作者问题下的 cmets 中所述。
  • 搜索树可以比平均速度快得多。虽然它可能有非常糟糕的边缘情况。
  • 尽管构造 C 数字可能需要 NlogN。 :) 但这似乎是一种有趣的方法。
猜你喜欢
  • 2019-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多