先说一下:
亟待解决的题
今天下午打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;
}
总结
这周学的二分查找法还是比较简单的,但是在边界问题上还需要自己多加探索,目前迈过了动态规划这个大难题,希望以后的问题会练的熟,写的快。