输出两行,第一行包括一个正整数n(n<=100000),代表数组长度。第二行包括n个整数,代表数组arr。
输出一行。代表你求出的最长的递增子序列。
9 2 1 5 3 6 4 8 9 7
1 3 4 8 9
5 1 2 8 6 4
1 2 4
其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个字典序最小,故答案为(1,2,4)
时间复杂度,空间复杂度
。
#include<bits/stdc++.h> using namespace std; int getIndex(vector<int>& v,int x) { int res = -1; int left = 0; int right = v.size()-1; while(left<=right) { int mid = (left+right)>>1; if(v[mid]>=x) { res = mid; right = mid-1; } else left = mid + 1; } return res; } vector<int> getLIS(vector<int>&v,vector<int>& dp) { // maxLen int maxLen = 0; int index = 0; for(int i=0;i<dp.size();++i) { // 字典序的话,这里要取>= if(dp[i]>=maxLen) { maxLen = dp[i]; index = i; } } //cout<<"maxlen "<<maxLen<<" index"<<index<<endl; vector<int>ans(maxLen); ans[--maxLen] = v[index]; vector<int>temp; for(int i=index;i>=0;--i) { if(v[i]<v[index] && dp[i]==dp[index]-1) { ans[--maxLen] = v[i]; index = i; } } return ans; } int main() { int n; cin>>n; vector<int>v(n); for(int i=0;i<n;++i) cin>>v[i]; vector<int>record; // dp[i] 以v[i]结尾的序列的最大长度 vector<int>dp(n); dp[0] = 1; record.push_back(v[0]); //int use = -1; for(int i=1;i<n;++i) { int l = getIndex(record,v[i]); if(l != -1) { record[l] = v[i]; dp[i] = l + 1; } else { record.push_back(v[i]); dp[i] = record.size(); } } //for(int i:dp) // cout<<i<<endl; vector<int> ans = getLIS(v,dp); for(int i : ans) cout<<i<<" "; }
#include <iostream> (720)#include <vector> using namespace std; vector<int> getSubSeqOnlogn(vector<int>& arr) { int n = arr.size(); vector<int> dp(n, 1); vector<int> ends(n, 0);//ends[i]表示长度为i+1的递增子序列的最小的结束元素 ends[0] = arr[0]; int right = 0; //ends的有效范围[0...right] int l = 0; int r = 0; int maxLen = 1; //最长递增子序列的长度 int maxInd = 0; //最长递增子序列结束元素的索引 for(int i = 1; i < n; i++) { l = 0; r = right; while(l <= r) {//查找arr[i]在ends有效范围上的插入位置,如果比其元素都大,则ends有效范围扩展 int mid = (l + r) >> 1; if(arr[i] > ends[mid]) { l = mid + 1; } else { r = mid - 1; } } right = max(right, l); //ends有效范围可能会扩展 ends[l] = arr[i]; //更新长度为i+1的递增子序列的结束元素 dp[i] = l + 1; //arr[i]为结束元素的最长递增子序列的长度 if(dp[i] >= maxLen) {//更新最大长度,由于题目有字典序最小的要求,=是必须的 maxLen = dp[i]; maxInd = i; } } vector<int> ans(maxLen); for(int i = maxInd; i >= 0; i--) { if(dp[i] == maxLen) { ans[--maxLen] = arr[i]; } } return ans; } int main() { int n; cin >> n; vector<int> arr(n); for(int i = 0; i < n; i++) { cin >> arr[i]; } vector<int> ans = getSubSeqOnlogn(arr); for(int i = 0; i < ans.size(); i++) { cout << ans[i] << " "; //题目对最后的空格很宽容,就这样吧 } return 0; }
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); String[] strArr = br.readLine().split(" "); int[] arr = new int[n]; for(int i = 0; i < n; i++){ arr[i] = Integer.parseInt(strArr[i]); } int[] dp = new int[n]; int[] ends = new int[n]; dp[0] = 1; ends[0] = arr[0]; int tail = 0; // 有效区域末尾 int maxLen = 1, maxIdx = 0; // 最长递增子序列长度和index for(int i = 1; i < n; i++){ int index = lowerbound(ends, 0, tail, arr[i]); dp[i] = index + 1; // 以i结尾的最长递增子序列长度为index+1 ends[index] = arr[i]; if(index > tail){ tail++; // 有效区扩充 } if(dp[i] >= maxLen){ maxLen = dp[i]; maxIdx = i; } } // 还原字典序最小的最长递增子序列 int[] increasingSeq = new int[maxLen]; for(int i = maxIdx; i >= 0; i--){ // 从后往前更新,遇到递增子序列长度为maxLen的arr[i]就更新结尾位置,然后把maxLen缩短 if(dp[i] == maxLen){ increasingSeq[--maxLen] = arr[i]; } } for(int i = 0; i < increasingSeq.length; i++){ System.out.print(increasingSeq[i] + " "); } } private static int lowerbound(int[] arr, int L, int R, int target) { int left = L, right = R; int index = R + 1; while(left <= right){ int mid = left + ((right - left) >> 1); if(arr[mid] < target){ left = mid + 1; }else{ index = mid; right = mid - 1; } } return index; } }
#include <bits/stdc++.h> using namespace std; int main(){ int n, l=0, k; scanf("%d", &n); int a[n], b[n], dp[n]={0}; for(int i=0;i<n;i++){ scanf("%d", &a[i]); int t = lower_bound(b, b+l, a[i]) - b; if(t == l){ b[l++] = a[i]; dp[i] = l; k = i; }else{ b[t] = a[i]; dp[i] = t+1; if(dp[i] == l) k = i; } } int r[l], m=l; r[--l] = a[k]; for(int i=k;i>=0;i--){ if(a[i]<a[k] && dp[i]==dp[k]-1){ r[--l] = a[i]; k = i; } } for(int i=0;i<m;i++) printf("%d ", r[i]); }
import java.util.*; import java.io.*; //这里用到了java自带的二分查找法,当然可以自己写,int binarySearch(int []arr,int s, int e, int key )。左闭右开。该方法要求数组非严格单调增。 //该方法自动查找到数组中第一个等于key的值的索引(假设arr中有key),返回该索引;如果没有则会找到第一个大于key的值的索引x。并返回: - x - 1。 //若数组中元素均小于key则返回: - len - 1。也就是可以利用返回值找到一个大于或等于key的值的索引。 //最好自己画图看看,很容易看出来。给一个设计好的测试用例 { 9,10,11,12, 8, 4, 15, -5, -4, -3, 7 }。 public class Main{ public static void main(String[] args)throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); String[] s = br.readLine().split(" "); int[] arr = new int[n]; for(int i = 0;i<n;i++){ arr[i] = Integer.parseInt(s[i]); } lengthOfLIS(arr); } public static int lengthOfLIS(int[] nums){ // 1.minTail[i]用来记录所有长度为i+1的递增序列中,末尾的最小值。可以用反正法证明minTail数组必然是一个单调递增数组。下面关键步骤是通过遍历nums数组逐渐的得到minTail // 2.dp[i] 记录以数组nums中第i个数字结尾的LIS的长度。 // 3.通多上面两个数组求出最小LIS int[] minTail = new int[nums.length];//minTail数组真实长度就是LIS长度,不确定,所以创建了一个和nums等长的数组,初始值均为0。 int[] dp = new int[nums.length];//数组dp长度等于nums长度 int len = 0;//表示minTail目前用到的长度 for (int l = 0; l < nums.length; l++){// 随着数组的遍历不断地跟新minTail // 搜索第一个大于nums[l]的数的位置。 int i = Arrays.binarySearch(minTail, 0, len, nums[l]); while(i>=0){//若查找到minTail中包含nums[l],接着二分查找直到找到第一个大于nums[l]的数 i = Arrays.binarySearch(minTail, i+1, len, nums[l]); } i = -i - 1;//得到大于nums[l]的第一个值的索引 // 已有序列都小于num if (i == len){ len++; } // 替换掉第一个大于或等于nums[l]的数,也就是说长为i+1的递增子序列最小值可以是更小的nums[l]。如果数组中没有比num大的数则num添加到末尾。 minTail[i] = nums[l]; dp[l] = i + 1; } // 下面代码用来找到按照字母表排序最小的最长递增子序列 int[] res = new int[len]; int index = res.length - 1; int next = Integer.MAX_VALUE; for (int k = nums.length - 1; k >= 0; k--){ if (nums[k] <= next && dp[k] == index + 1){//满足该条件求得的序列就是目标LIS。假设已知LIS最后一个数字(其实就是minTail中最后一个非0值 //通过该判断求LIS前一个数值?(首先该条件为nums[k]是LIS倒数第二个数值的充分条件,但还需证明由该条件得到的LIS按字典排序最小)。假设除了k满足该条件, //还有i,j...满足,那么i,j...不可能在k之后(因为倒着遍历nums),所以推出num[i,j,...]必然大于nums[k]。绝不可能小于或者等于,否则可以推出dp[k]==index+2 res[index] = nums[k]; next = res[index]; index--; } } StringBuilder sb = new StringBuilder(); for (int val : res) sb.append(val).append(" "); System.out.println(sb.toString()); return len; } }
#include <vector>
using namespace std;
int maxNumber(int a, int b);
vector<int> getDP(vector<int> arr);
vector<int> getList(vector<int> arr, vector<int> dp);
int main()
{
int CntOfNumbers = 0;
int numberOfArray = 0;
vector<int> array;
vector<int> result;
vector<int> dp;
//输入数组大小
cin >> CntOfNumbers;
//数组元素
for(int i = 0; i < CntOfNumbers;i++)
{
cin >> numberOfArray;
array.push_back(numberOfArray);
}
//dp[i]表示遍历到arr[i]时,arr[0...i]组成的最长子序列长度
dp = getDP(array);
result = getList(array, dp);
int length = result.size();
for(int i = 0; i < length; i++)
{
cout << result[i] << " ";
}
return 0;
}
int maxNumber(int a, int b)
{
return a > b ? a : b;
}
vector<int> getDP(vector<int> arr)
{
int length = arr.size();
vector<int> dp(length, 0);
vector<int> end(length, 0);
//有效区的右边界会更新
int right = 0;
int l = 0, m = 0, r = 0;
end[0] = arr[0];
dp[0] = 1;
//开始对后面的数字用二分法更新end数组和dp数组
for(int i = 1; i < length; i++)
{
l = 0;
r = right;
//找到end中最左边大于或等于arr[i]的位置,或者直接大于end最右边的数,l走到了right+1处
while(l <= r)
{
m = (l + r)/2;;
if(arr[i] > end[m])
l = m + 1;
else
r = m - 1;
}
//扩大有效区
right = maxNumber(right, l);
//更新数组
end[l] = arr[i];
dp[i] = l + 1;
}
return dp;
}
vector<int> getList(vector<int> arr, vector<int> dp)
{
int length = arr.size();
//最长子序列的长度和对应的下标
int len = 0;
int index = 0;
for(int i = 0; i < length; i++)
{
//滑到最右边那个最大的dp[i]和下标
//因为当dp相同时,最右边那个数最小,所以接下来第一个找到符合要求的数就可以直接存入数组中
if(dp[i] >= len)
{
len = dp[i];
index = i;
}
}
//开始寻找最小值的最长子序列
vector<int> list(len, 0);
//先放置最后那个数
list[--len] = arr[index];
//从后往前遍历
for(int i = index - 1; i >= 0; i--)
{
//要满足两个条件
if(arr[i] < arr[index] && dp[i] == (dp[index] - 1))
{
list[--len] = arr[i];
index = i;
}
}
return list;
}
//二分查找+贪心 #include "bits/stdc++.h" using namespace std; int midsearch(int target,vector<int>& arr1,vector<int>& arr){ //arr1中第一个对应到arr中的值大于等于target的下标 int l=0,r=arr1.size(); int mid; while(l<r){ mid=(l+r)>>1; if(arr[arr1[mid]]==target) { while(mid-1>=0&&arr[arr1[mid-1]]==target) mid--; return mid; } else if(arr[arr1[mid]]>target) r=mid; else if(arr[arr1[mid]]<target) l=mid+1; } return l; } int main() { //贪心+二分查找可以求出最大递增子序列长度 //怎么得到最大递增子序列? //arr1[i]表示递增长度最大长度为i时,使第i个值最小的arr的下标 //dp[i]表示arr[i]作为其中一个值时,它的的前一个值的下标 int len;cin>>len; vector<int> arr(len); for(int i=0;i<len;i++) cin>>arr[i]; vector<int> dp(len,-1); vector<int> arr1; arr1.push_back(0); for(int i=1;i<len;i++){ int target=arr[i]; if(target>arr[arr1.back()]){ dp[i]=arr1.back(); arr1.push_back(i); } else{ int t=midsearch(target, arr1,arr); arr1[t]=i; if(t>0) dp[i]=arr1[t-1]; } } int len1=arr1.size(); vector<int> ret(len1,0); ret[len1-1]=arr[arr1.back()]; int j=len1-2; int last=dp[arr1.back()]; //for(int i=0;i<len;i++)cout<<dp[i]; while(j>=0){ ret[j]=arr[last]; j--; last=dp[last]; } for(int i=0;i<len1;i++){ cout<<ret[i]<<' '; } return 0; }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; public class Main { public static void longestIncreasingSequence(int[] num) { int[] arr = new int[num.length]; int[] dp = new int[num.length]; int sum = 0; int idx; for (int i = 0; i < num.length; ++i) { idx = Arrays.binarySearch(arr, 0, sum, num[i]); while (idx > 0) { idx = Arrays.binarySearch(arr, idx + 1, sum, num[i]); } if (sum == -idx - 1) { sum++; } arr[-idx - 1] = num[i]; dp[i] = -idx; } int[] res = new int[sum]; int index = res.length - 1; int next = Integer.MAX_VALUE; for (int k = num.length - 1; k >= 0; --k) { if (num[k] <= next && dp[k] == index + 1) { res[index] = num[k]; next = res[index]; if ((--index) < 0) { break; } } } StringBuilder builder = new StringBuilder(""); for (int re : res) { builder.append(re).append(" "); } System.out.println(builder.toString()); } public static void main(String[] args) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(bufferedReader.readLine()); String[] str = bufferedReader.readLine().split(" "); int[] num = new int[n]; for (int i = 0; i < n; ++i) { num[i] = Integer.parseInt(str[i]); } longestIncreasingSequence(num); } }
import java.util.*; import java.io.*; public class Main { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); int n = Integer.parseInt(in.readLine()); String[] t = in.readLine().split(" "); int[] nums = new int[n]; for (int i = 0; i < n; i++) nums[i] = Integer.parseInt(t[i]); int[] q = new int[n + 10], f = new int[n + 10]; int len = 0, idx = 0; for (int i = 0; i < n; i++) { int l = 0, r = len; while (l < r) { int mid = l + r + 1 >> 1; if (q[mid] < nums[i]) l = mid; else r = mid - 1; } q[l + 1] = nums[i]; f[i] = Math.max(f[i], l + 1); if (f[i] >= len) { len = f[i]; idx = i; } } int[] ans = new int[len]; for (int i = idx; i >= 0; i--) { if (f[i] == len) ans[--len] = nums[i]; } for (int i = 0; i < ans.length; i++) out.write(ans[i] + " "); out.flush(); } }
def solution(nums): stack = [] pre_idx = [-1] * len(nums) for idx, num in enumerate(nums): if not stack&nbs***bsp;num > nums[stack[-1]]: stack.append(idx) pre_idx[idx] = stack[-2] if len(stack) > 1 else -1 else: l, r = 0, len(stack) - 1 loc = r while l <= r: mid = (l + r) // 2 if nums[stack[mid]] >= num: loc = mid r = mid - 1 else: l = mid + 1 stack[loc] = idx pre_idx[idx] = stack[loc - 1] if loc >0 else -1 idx = stack[-1] res = [] while idx != -1: res = [str(nums[idx])] + res idx = pre_idx[idx] return res # return [str(item) for item in val_idx] n = map(int, input().strip()) array = list(map(int, input().strip().split())) # print(array) res = solution(array) print(' '.join(res))
#include <iostream> #include <algorithm> #include <string.h> #include <vector> using namespace std; const int MAXN = 1e5 + 10; int n; int arr[MAXN], pre[MAXN]; int main(){ cin >> n; for(int i = 0; i < n; i ++) cin >> arr[i]; vector<int> dp = {-1}; for(int i = 0; i < n; i ++){ int lp = 0, rp = dp.size() - 1; while(lp < rp){ int mid = lp + (rp - lp + 1) / 2; int idx = dp[mid]; if(idx == -1 || arr[i] > arr[idx]) lp = mid; else rp = mid - 1; } if(lp == dp.size() - 1) dp.push_back(i); else dp[lp + 1] = i; pre[i] = dp[lp]; } vector<int> rnt; int pt = dp.back(); while(pt != -1){ rnt.push_back(arr[pt]); pt = pre[pt]; } for(int i = rnt.size() - 1; i >= 0; i --) cout<<rnt[i]<<" "; cout<<endl; return 0; }
#include <bits/stdc++.h> using namespace std; // 得到dp数组 static void getdp(vector<int> &v, vector<int> &dp, int &maxlen){ int sz = v.size(); vector<int> ends(sz, 0); dp[0] = 1; ends[0] = v[0]; int right = 1; for(int i=1;i<sz;i++){ int fleft = 0; int fright = right; while(fleft < fright){ int mid = fleft + ((fright-fleft)>>1); if(ends[mid] < v[i]) fleft = mid + 1; else fright = mid; } dp[i] = fleft + 1; ends[fleft] = v[i]; if(fright == right) ++right; } maxlen = right; return ; } static void getminseq(vector<int> &v, vector<int> &res, vector<int> &dp, int maxlen){ int find = maxlen; int sz = v.size(); vector<int> limit(maxlen+1, 0); int pos = maxlen; // 构造limit,查找的右边界 for(int i=sz-1;i>=0;--i){ if(dp[i] == find){ limit[pos--] = i; find--; } } pos = 0; int left = 0, right = 0, minpos=0, minvalue=0; for(int target=1;target<=maxlen;target++){ minvalue=INT_MAX; left = minpos; // 上一次的最优解位置作为左边界 right=limit[target]; // limit作为右边界 for(int k=left;k<=right;k++){ // 范围内查找最小元素 if(dp[k] == target){ if(v[k] < minvalue){ minvalue = v[k]; minpos = k; } } } res[pos++] = minvalue; } return; } int main(){ int n; cin>>n; vector<int> v(n); for(int i=0;i<n;i++) cin>>v[i]; vector<int> dp(n, 0); int maxlen; getdp(v, dp, maxlen); vector<int> res(maxlen, 0); getminseq(v, res, dp, maxlen); for(int num : res) cout<<num<<" "; return 0; }
#include <iostream> using namespace std; #define N 100000 int arr[N]; int dp[N]; int my_ends[N]; int record[N]; int main() { int n; cin>>n; for(int indx=0;indx<n;++indx) { cin>>arr[indx]; } // dp process dp[0]=1; int right=0; my_ends[0]=arr[0]; int l=0; int r=0; int m=0; int dp_max_rec=1; int dp_max_indx=0; for(int i=1;i<n;++i) { l=0; r=right; while(l<=r) { m=(l+r)/2; if(arr[i]>my_ends[m]) l=m+1; else r=m-1; } dp[i]=l+1; my_ends[l]=arr[i]; if(l>right) right=l; if(dp[i]>=dp_max_rec) { dp_max_rec=dp[i]; dp_max_indx=i; } } record[dp_max_rec-1]=dp_max_indx; int cur=dp_max_rec-1; for(int indx=dp_max_indx-1;indx>=0;--indx) { if(cur<=0) { break; } if(dp[indx]==dp[record[cur]]-1&&arr[indx]<arr[record[cur]]) { --cur; record[cur]=indx; continue; } if(dp[indx]==dp[record[cur]]&&arr[indx]<arr[record[cur]]) { record[cur]=indx; continue; } if(dp[indx]>dp[record[cur]]) { if(arr[indx]<arr[record[dp[indx]-1]]) { cur=dp[indx]-1; record[cur]=indx; continue; } } } for(int indx=0;indx<dp_max_rec;++indx) { cout<<arr[record[indx]]; if(indx==dp_max_rec-1) cout<<'\n'; else cout<<' '; } return 0; }
#include<iostream> (720)#include<algorithm> using namespace std; int *arr; int *dp; int *bin; int *lis; //二分查找利用bin数组能优化dp数组的计算速度 int BinarySearch(int *bin, int len, int n){ int left = 1; int right = len; while (left < right){ int mid = (left + right) / 2; if (bin[mid] > n){ right = mid; } else{ left = mid + 1; } } return right; } int getResult1(int n){ dp[0] = 1; bin[1] = arr[0]; int index = 1; for (int i = 1; i < n; i++){ if (arr[i] > bin[index]){ bin[++index] = arr[i]; dp[i] = index; } else{ int temp = BinarySearch(bin, index, arr[i]); bin[temp] = arr[i]; dp[i] = temp; } } return index; } int main(){ int n; cin >> n; arr = new int[n]; dp = new int[n + 1]; bin = new int[n + 1]; for (int i = 0; i < n; i++){ cin >> arr[i]; } //根据dp数组计算lis数组 int ans = getResult1(n); int length = ans; lis = new int[ans]; int index = 0; for (int i = n - 1; i >= 0; i--){ if (dp[i] == ans){ lis[--ans] = arr[i]; index = i; break; } } index--; for (int i = index; i >= 0; i--){ if (dp[i] == ans){ lis[--ans] = arr[i]; } } for (int i = 0; i < length; i++){ cout << lis[i] << endl; } delete[] arr; delete[] dp; delete[] bin; delete[] lis; return 0; }
//这个本地所有测试样例都可以通过,牛客超时,应该需要用二分 #include<cstdio> #include<vector> using namespace std; vector<int> dp; vector<int> v; int choice[100010]; vector<int> maxpath; int main() { int n; scanf("%d", &n); dp.resize(n); v.resize(n); maxpath.clear(); fill(choice, choice + 100010, -1); //choice.resize(n); for (int i = 0; i < n; ++i) { scanf("%d", &v[i]); dp[i] = 1; } int mmax = 1, mmaxi = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { if (v[j] < v[i]) { if (dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; choice[i] = j; } else if (dp[j] + 1 == dp[i]) { if (v[choice[i]] >=v[j]) { choice[i] = j; } } } } if (mmax <= dp[i]) { mmax = dp[i]; mmaxi = i; } } while (mmaxi != -1) { maxpath.push_back(mmaxi); mmaxi = choice[mmaxi]; } int flag = 0; for (int i = maxpath.size() - 1; i >= 0; --i) { if (flag == 0) { printf("%d", v[maxpath[i]]); flag = 1; } else { printf(" %d", v[maxpath[i]]); } } return 0; }