ACM周记 - 二分法

先说一下:

亟待解决的题

今天下午打CodeForces第二题,我至今还是不知道错在哪QAQ。
第二题就是给定一个数组,求出让数组升序的最少移动次数。

以下是我自己的思路,但是出错了。

这个题包括两种情况,一种是交换位置,另一种是插入。
刚开始我只注意到了交换位置,但是尝试时发现第三个数据没过。
这时我突然想到一个自方法:

先求出最长不下降子序列元素个数,然后用原数组个数一减就出来了。

于是有了这段代码:

#include <iostream>
#include <algorithm>
 
using namespace std;
 
int a[1000],f[1000];
 
int maxl(int len) {
    int maxn = 1;
    for (int i=0; i<len; i++) {
        if (f[i] >= maxn && a[len] >= a[i]) {
            maxn = f[i] + 1;
        }
    }
    return maxn;
}
int main() {
    int t;
    cin >> t;
    while(t--) {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        f[0]=1;
        for(int i = 1; i < n; i++) {
            f[i] = maxl(i);
        }
        sort(f, f + n);
        cout << n - f[n-1] << endl;
    }
    return 0;
}

测试发现没错,一提交,说第二组数据没过。
于是我仔细想了一下,发现了一个问题:
这种方法虽然考虑了移动插入这种情况,但是忽略了直接交换的情况,比如:

5 12 7 8 9 6

他会输出2,但是交换12和6的位置只需一次。于是我加上考虑交换的代码,用min求一下两种情况中的较小者:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

int a[1000],b[1000],f[1000];
int maxl(int len) {
    int maxn = 1;
    for (int i = 0; i < len; i++) {
        if (f[i] >= maxn && b[len] >= b[i]) {
            maxn = f[i] + 1;
        }
    }
    return maxn;
}

int main()
{
    int i, j, minn = 0x3f, n, m, t;
    cin >> t;
    while(t--)
    {   int k = 0;
        cin >> n;
        for(i = 0; i < n; i++)
        {   cin >> a[i];
            b[i] = a[i]; 
        }
        sort(a, a + n);
        for(i = 0; i < n; i++) if(a[i] != b[i]) k++;
        if(k % 2 == 0) k = k / 2;
        else k = (k - 1) / 2;
        f[0] = 1;
        for(int i = 1; i < n; i++) {
            f[i] = maxl(i);
        }
        sort(f, f + n);
        cout << min(k, n - f[n - 1]) << endl;
    }
}

这次测试了我能想到的所有情况,但是还是在第二组测试数据报错,到底错在哪里了呢?希望我之后能想明白。

二分法

这周还学了二分法,因为没有vjudge上的题,就简单说一下自己的收获吧。
二分法自身是比较简单的。有时问题会涉及区间的范围和单调性,用二分法是不错的选择。
上课时有个题不太清楚就在这简单写一下自己之后想通的思路吧。

POJ 3258 River Hopscotch

一条河的长度已知,河中间有一些石头,石头的数量已知,现在可以移除一些石头,问移除m块石头后,相邻两块石头之间的距离最小值最大是多少。

不会的原因是不理解“最小值的最大”到底是个啥。。。这个思考了好久,一眨眼已讲完了。后来自己看代码终于看了。
而且那个内联函数有点半生不熟,只能先搁置一下。

思路:

主要思路就是每次二分枚举一个值,判断该值能够去掉多少块石头,通过二分法枚举求上限。二分这个最短距离,然后求出如果这个是最短距离,我们需要在原来的基础上去掉几个石头。
这里有个上限条件,就是:
去掉石头的个数如果比M大,说明我们枚举的最短距离比真正的最短距离大了。
去掉的石头个数比M小,说明我们枚举的最短距离比真正大最短距离小了。
这次方便定义了一个自定义函数。代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

#define mmset(a,b) memset(a,b,sizeof(a))

using namespace std;

const int N = 0x3f;
int data[N], n, m, k;
int count1(int mark);

int main()
{
	while (~scanf("%d %d %d", &n, &m, &k))
	{
		m += 2;
		data[1] = 1, data[m] = n;
		for (int i = 2; i < m; i++)
		{
			scanf("%d", &data[i]);
		}
		sort(data + 1, data + 1 + m);
		int a = 1, b = n, m;
		while (a <= b)
		{
			m = (a + b) / 2;
			int temp = count1(m);
			if (temp <= k)
			{
				a = m + 1;
			}
			else
			{
				b = m - 1;
			}
		}
		printf("%d\n", b);

	}
	return  0;
}
int count1(int mark)
{
	int res = 0, temp = 0;
	for (int i = 2; i <= m - 1; i++)
	{
		if (data[i] - data[i - 1] + temp < mark)
		{
			res++;
			temp += data[i] - data[i - 1];
		}
		else
		{
			temp = 0;
		}
	}
	return res;
}

总结

这周学的二分查找法还是比较简单的,但是在边界问题上还需要自己多加探索,目前迈过了动态规划这个大难题,希望以后的问题会练的熟,写的快。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值