我最近在研究以下问题。 http://www.codechef.com/problems/D2
厨师正计划为DirectiPlex就职典礼举办自助餐,并邀请所有人参加。在他们的路上,每位客人拿起一张包含随机数的纸(这个数字可能会重复)。然后客人和他们的朋友坐在圆桌旁。 厨师现在决定他想玩游戏。他要求你从桌子上挑选一个随机的人,让他们大声读出他们的号码。然后,围绕桌子顺时针移动,每个人都会读出他们的号码。目标是找到形成不断增加的子序列的数字集。拥有这些数字的所有人都有资格参加幸运抽奖!其中一位软件开发人员对这一前景感到非常兴奋,并希望最大限度地增加有资格参加抽奖的人数。因此,他决定编写一个程序,决定谁应该首先阅读他们的号码,以便最大化有资格参加抽奖的人数。你能打败他吗?
输入
第一行包含t
,测试用例数(约15)。然后是t
个测试用例。每个测试用例包含两行:
N
,即邀请参加聚会的嘉宾人数。N
个数字a1, a2, ..., an
,这些是以顺时针顺序写在纸张上的数字。输出
对于每个测试用例,请打印包含单个号码的行,该号码是有资格参加抽奖的最大客人人数。
约束
1 ≤ N ≤ 10000
您可以假设纸张上的每个号码; ai
是随机生成的,即来自区间[0,U]
的任何数字的概率相等,其中U
是某个上限(1 ≤ U ≤ 106
)。
示例
输入:
3
2
0 0
3
3 2 1
6
4 8 6 1 5 2
输出:
1
2
4
在检查解决方案时,我找到了这段代码:
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
#define LIMIT 37
using namespace std;
struct node {
int val;
int index;
};
int N;
int binary(int number, vector<int>& ans) {
int start = 0;
int n = ans.size();
int end = n - 1;
int mid;
if (start == end)
return 0;
while (start != end) {
mid = (start + end) / 2;
if (ans[mid] == number)
break;
if (ans[mid] > number)
end = mid;
else
start = mid + 1;
}
mid = (start + end) / 2;
return mid;
}
void display(vector<int>& list) {
cout << endl;
for (int i = 0; i < list.size(); i++)
cout << list[i] << " ";
cout << endl;
}
int maxsubsequence(vector<int>& list) {
vector<int> ans;
int N = list.size();
ans.push_back(list[0]);
int i;
// display(list);
for (i = 1; i < N; i++) {
int index = binary(list[i], ans);
/*if(index+1<ans.size())
continue;*/
if (list[i] < ans[index])
ans[index] = list[i];
if (list[i] > ans[index])
ans.push_back(list[i]);
// display(ans);
}
return ans.size();
}
int compute(int index, int* g) {
vector<int> list;
list.push_back(g[index]);
int itr = (index + 1) % N;
while (itr != index) {
list.push_back(g[itr]);
itr = (itr + 1) % N;
}
return maxsubsequence(list);
}
int solve(int* g, vector<node> list) {
int i;
int ret = 1;
for (i = 0; i < min(LIMIT, (int)list.size()); i++) {
// cout<<list[i].index<<endl;
ret = max(ret, compute(list[i].index, g));
}
return ret;
}
bool cmp(const node& o1, const node& o2)
{ return (o1.val < o2.val); }
int g[10001];
int main() {
int t;
cin >> t;
while (t--) {
cin >> N;
vector<node> list;
int i;
for (i = 0; i < N; i++) {
node temp;
cin >> g[i];
temp.val = g[i];
temp.index = i;
list.push_back(temp);
}
sort(list.begin(), list.end(), cmp);
cout << solve(g, list) << endl;
}
return 0;
}
有人可以向我解释一下。我很清楚在nlog(n)中计算LIS。 我无法理解的是这部分:
int ret = 1;
for (i = 0; i < min(LIMIT, (int)list.size()); i++) {
// cout<<list[i].index<<endl;
ret = max(ret, compute(list[i].index, g));
}
以及排序背后的原因
sort(list.begin(),list.end(),cmp);
答案 0 :(得分:0)
这个算法只是猜测起点并计算每个猜测的LIS。
LIS中的第一个值可能是一个小数字,因此该算法只是将LIMIT最小值作为潜在起点。
sort函数用于识别最小值。
for循环用于依次检查每个起点。
请注意,此算法可能会因某些输入而失败。例如,考虑序列
0,1,2,..,49,9900,9901,...,99999,50,51,52,...,9899
该算法将仅尝试前37个起点,并在50处错过最佳起点。
您可以通过将代码更改为:
来测试int main() {
int t;
t=1;
while (t--) {
N=10000;
vector<node> list;
int i;
for (i = 0; i < N; i++) {
node temp;
if (i<50)
g[i]=i;
else if (i<150)
g[i]=9999-150+i;
else
g[i]=i-100;
temp.val = g[i];
temp.index = i;
list.push_back(temp);
}
sort(list.begin(), list.end(), cmp);
cout << solve(g, list) << endl;
}
return 0;
}
这将根据LIMIT是37还是370来生成不同的答案。
在实践中,对于随机生成的序列,它将有很好的工作机会(虽然我不知道如何精确计算概率)。