一個有意思的題。
很顯然,這是LIS問題的加強版。
對於每一個詢問,我們從前到後檢查每一個元素,如果f[i]>=x那麽輸出,然後x--。如果x最終為零,那麽說明有解。
證明:
首先,由於我們是從前考慮的每一個元素,所以保證了字典序最小。
其次,因為如果對於一個元素i|f[i]>x,那麽從i後面一定至少能夠找到x個元素使得構成一個長度為x的序列。
考慮到字典序的定義,這樣貪心一定是最優的。
我們處理LIS時,令g[i]為使f[j]==i的最大j,這樣可以用二分查照優化轉移,復雜度從O(n2)降到了O(nlogn)
當然,這個題目不優化,直接使用O(n2)的算法也完全可以卡過。
下面上代碼。
#include <bits/stdc++.h> using namespace std; const int maxn = 10001; struct num { int value, pos; bool operator < (const num& b) const { return this->pos<b.pos; } } a[maxn]; int n, m, Ma; int f[maxn]; void getlis() { f[n-1] = 1; for(int i = n-2; i >= 0; i--) { int ans = 0; for(int j = i+1; j < n; j++) { if(a[j].value>a[i].value && f[j] > ans){ ans = f[j]; } } f[i] = ans+1; } } void getlis2() { f[n-1] = 1; int g[maxn]; //define g[i] to be the max j that let f[j] = i memset(g, -1, sizeof(g)); g[0] = 0x3f3f3f; for(int i = n - 1; i >= 0; i--) { int ans = 0; int L = 0; int R = n; while(L < R) { if(R-L <= 1) break; //二分查找最小的比x大的元素 int mid = (L+R)/2; if(g[mid] > a[i].value) L = mid; else R = mid; } ans = L; f[i] = ans+1; g[ans+1] = max(g[ans+1], a[i].value); } } void solve() { sort(a, a+n); scanf("%d", &m); while(m--) { int l; scanf("%d", &l); int x = l; int cnt = 0; int b[maxn]; int lastpos = 0; for(int i = 0; i < n; i++) { if(f[a[i].pos] >= x && a[i].value> lastpos) { b[cnt++] = a[i].value; x--; lastpos = a[i].value; } } if(cnt < l-1) printf("%s\n", "Impossible"); else { for(int i = 0; i < l; i++) { printf("%d", b[i]); if(i!=l-1) putchar(' '); } printf("\n"); } } } int main() { scanf("%d", &n); for(int i = 0; i < n; i++) { int x; scanf("%d", &x); a[i] = {x, i}; Ma = max(Ma, x); } getlis2(); // for(int i = 0; i < n; i++) cout << f[i] << ' '; solve(); }
Tags: 動態規劃 字典序 貪心
文章來源: