Poj 1065 wooden sticks

题意大概是,有很多木块,需要进行加工,如果木块的长度和重量都大于等于之前加工的,就不需要等待,否则需要等待一分钟,问加工这些木块最少需要多少时间。

这个问题如果按照长度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;
}

猜你喜欢

转载自blog.csdn.net/zerooffdate/article/details/81569572