题意大概是,有很多木块,需要进行加工,如果木块的长度和重量都大于等于之前加工的,就不需要等待,否则需要等待一分钟,问加工这些木块最少需要多少时间。
这个问题如果按照长度l排序,然后根据重量w排序,之后就只需要求排好序的数列里面,单调不递减子序列的最少数量N。
方法一:直觉贪心
这个题目有两个AC的方法。先说一个能AC,但是不能证明正确的,就是贪心算法,从头到尾遍历数组,每次都拿第一个比当前最重的木块,去加工(由于长度已经排好序了,所以只要看重量就行了)。模拟之后,得到加工的答案。
这个答案的代码下面贴出来。之所以这么做,是因为这个方法虽然简单易懂能AC,但是我怎么都想不到办法去证明这个方法的正确性。所以我有看了下别人怎么做的。
方法二:求最长单调递减序列LDS
然后就找到了这篇文章,嗯。总算找到能符合数学解释的答案了,开心。这里讲一下我的理解。
先说结论:最少单调非递减序列的数量minN,等于最长单调递减长度maxL
这个结论看起来有点风马牛不相及,但是看了下面的证明,就能了解为什么是这样了。
证明:
1,设,存在一个x,令x<maxL成立
2,将这个最长单调递减序列取出来,得到一个没有序列的数组分成x个单调非递减序列。
3,设法将maxL个取出来的队列,放回到这x个单调非递减序列里面。
4,根据鸽笼定理,由于x < maxL ,可以得出,不管怎么安排这maxL个元素,都至少存在一个数组,需要按顺序同时放入maxL中的两个元素,破坏了序列单调非递减的性质。
至此,求得最少单调非递减序列数量为maxL,不会更小。
剩下的就是关于怎么求LIS了,使用贪心+二分搜索,可以在nlogn时间内求到结果。这里就不赘述。
引用一下码农场的代码:
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
pair<int, int> stick[5000 + 16];
int dp[5000 + 16]; // dp[i] := 长度为i+1的下降子序列中末尾元素的最大值
///SubMain//
int main(int argc, char *argv[])
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
for (int i = 0; i < n; ++i)
{
cin >> stick[i].first >> stick[i].second;
}
sort(stick, stick + n);
memset(dp, -1, n * sizeof(int));
for (int i = 0; i < n; ++i)
{
*lower_bound(dp, dp + n, stick[i].second, greater<int>()) = stick[i].second;
}
cout << lower_bound(dp, dp + n, -1, greater<int>()) - dp << endl;
}
return 0;
}
然后这里是方法一的代码:
#include <string.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
class stick{
public:
int l;
int w;
};
int cmp(const void *a, const void* b)
{
const stick sa = *(const stick*)a, sb = *(const stick*)b;
if(sa.l == sb.l)
{
return sa.w - sb.w;
}
else
{
return sa.l - sb.l;
}
}
stick sticks[5005];
int used[5005];
int min_count = 0;
int cal_min(int len)
{
qsort(sticks, len, sizeof(stick), cmp);
int counter = 0;
for(int i = 0; i < len; i++)
{
int lastW = sticks[i].w;
if(used[i] == 0)
{
//not used
used[i] = 1;
counter ++;
for(int j = i + 1; j < len; j++)
{
if(used[j] == 0 && sticks[j].w >= lastW)
{
used[j] = 1;
lastW = sticks[j].w;
}
}
}
}
return counter;
}
int main(int argc, char *argv[])
{
int t = 0;
cin >> t;
for (int i = 0; i < t; ++i) {
int len = 0;
cin >> len;
memset(sticks, 0, sizeof(sticks));
memset(used, 0, sizeof(used));
for (int j = 0; j < len; ++j) {
cin >> sticks[j].l >> sticks[j].w;
}
if(i == t - 1)
{
cout << cal_min(len);
}
else
{
cout << cal_min(len) << endl;
}
}
return 0;
}