POJ3670Eating Together(LIS最长(非)上升(下降)子序列模板)

来源:互联网 发布:大数据搜索引擎技术 编辑:程序博客网 时间:2024/05/29 03:09
Eating Together
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 6034 Accepted: 2941

Description

The cows are so very silly about their dinner partners. They have organized themselves into three groups (conveniently numbered 1, 2, and 3) that insist upon dining together. The trouble starts when they line up at the barn to enter the feeding area.

Each cow i carries with her a small card upon which is engraved Di (1 ≤ Di ≤ 3) indicating her dining group membership. The entire set of N (1 ≤ N ≤ 30,000) cows has lined up for dinner but it's easy for anyone to see that they are not grouped by their dinner-partner cards.

FJ's job is not so difficult. He just walks down the line of cows changing their dinner partner assignment by marking out the old number and writing in a new one. By doing so, he creates groups of cows like 111222333 or 333222111 where the cows' dining groups are sorted in either ascending or descending order by their dinner cards.

FJ is just as lazy as the next fellow. He's curious: what is the absolute mminimum number of cards he must change to create a proper grouping of dining partners? He must only change card numbers and must not rearrange the cows standing in line.

Input

* Line 1: A single integer: N
* Lines 2..N+1: Line i describes the i-th cow's current dining group with a single integer: Di

Output

* Line 1: A single integer representing the minimum number of changes that must be made so that the final sequence of cows is sorted in either ascending or descending order

Sample Input

513211

Sample Output

1

Source

USACO 2008 February Silver

题目大意:给你n个数,每个数都只能是1到3任意一个数字,然后要求修改最小的次数,完成一个非降序或者非上序的数组,问最小操作次数是多少

解题思路:这道题是一开始纯暴力写了无数遍,加上类似回文串啊上升序列啊这种遇到必死的题,所以没办法去网上找解法,发现我看不懂,毕竟没有注释什么的,解题思路太简单,然后就没看,之后发现他们同时提到了算法就是LIS最长上升子序列,然后就自学了一下,这道题主要是总结一下LIS

LIS这个算法是由两部分组成,一部分是用数组lis[]存子序列中的值,一个是二分

第一部分数组lis[]存取子序列每一个位置的值得,一般遍历给的数组a[]的时候,会出现以下两种情况,我们假设此刻的子序列长度为len,我的讲解都是按照非降序子序列讲解的

1.lis[len]<=a[i]这意味着子序列最末位置的数值<=此刻我遍历到的a[]元素,意味着a[i]符合条件,我可以把他加到子序列长度当中,所以此刻

lis[++len]=a[i];

2.lis[len]>a[i]意味着子序列最末位置的数值<遍历到的a[]元素,那我就用二分找到某个位置lis[k]<=a[i]<lis[k+1],然后lis[k+1]=a[i]我这样做的原因是我希望他这个子序列的每个元素尽可能的小,这样我才能尽可能的往里面加元素,使之尽可能的长,换句话说,我希望在子序列中的元素之间的差值尽可能小,然后我就可以尽可能增长子序列了,这是我自己想的,若是不太理解的话

这有一个大神的关于LIS的一个讲解,我是看这个懂得,可以去看看

最长上升子序列(LIS)长度的O(nlogn)算法


#include<iostream>    #include<cstdio>  #include<stdio.h>  #include<cstring>    #include<cstdio>    #include<climits>    #include<cmath>   #include<vector>  #include <bitset>  #include<algorithm>    #include <queue>  #include<map>  using namespace std;int lis[300005], a[300005], n, i, len, l, r, ans, k;int main(){cin >> n;for (i = 1; i <= n; i++){cin >> a[i];}memset(lis, 0, sizeof(lis));lis[1] = a[1];len = 1;for (i = 2; i <= n; i++)//升序{if (lis[len] <= a[i]){lis[++len] = a[i];}else{l = 1;r = len;while (l<r){k = (l + r) / 2;if (lis[k] > a[i]){r = k;}else{l = k + 1;}}lis[l] = a[i];}}ans = len;len = 0;memset(lis, 0, sizeof(lis));lis[1] = a[1];len = 1;k = 0;for (i = 2; i <= n; i++)//降序{if (lis[len] >= a[i]){lis[++len] = a[i];}else{l = 1;r = len;while (l<r){k = (l + r) / 2;if (lis[k] < a[i]){r = k;}else{l = k + 1;}}lis[l] = a[i];}}cout << min(n - ans, n - len) << endl;}


0 0
原创粉丝点击