1. 程式人生 > 實用技巧 >GJGHFD的排列 題解 [DP]

GJGHFD的排列 題解 [DP]

GJGHFD的排列

Description:

​ 給定一個長度為 \(n\) 的排列 \(p_1\), \(p_2\), · · · , \(p_n\),你可以做以下兩個操作之一:

​ 1.將 \(p_{n−1}\) 移到 \(p_1\) 之前,即 \(p_1\), \(p_2\), · · · , \(p_n\) \(\rightarrow\) \(p_{n−1}\), \(p_1\), \(p_2\), · · · , \(p_{n−2}\), \(p_n\).

​ 2.將 \(p_{1}\) 移到 \(p_n\) 之後,即 \(p_1\), \(p_2\), · · · , \(p_n\)

\(\rightarrow\) \(p_2\), \(p_3\), · · · ,\(p_{n−1}\), \(p_n\), \(p_{1}\).

​ 一串連續的 \(1\) 操作稱為一次 \(3\) 操作. 求使得原排列變為 \(1, 2, · · · , n\) 的操作序列中包含的 \(3\) 操作的最小次數.

Input:

​ 第一行一個整數 \(n\),表示排列長度.
​ 接下來一行 \(n\) 個整數,給出這個排列.

Output:

​ 一行一個整數表示答案.

Sample Input:

6
2 4 5 1 3 6

Sample Output:

2

樣例解釋:

​ 首先做 \(5\)

\(2\) 操作,原排列變為 \(6, 2, 4, 5, 1, 3\);
​ 再做 \(3\)\(1\) 操作,原排列變為 \(4, 5, 1, 6, 2, 3\);
​ 再做 \(4\)\(2\) 操作,原排列變為 \(2, 3, 4, 5, 1, 6\);
​ 最後做 \(3\)\(1\) 操作,原排列變為 \(1, 2, 3, 4, 5, 6\) .

Hint:

​ 對於\(100\%\)的資料,\(1 \leq n \leq 500\)

​ 時間限制: \(1s\)

​ 空間限制: \(512M\)

題目分析:

​ 我們經過手玩一下操作 \(3\) ,可以發現其實操作 \(3\)

的效果等價於把序列中任意元素取出來,扔到序列中的任意位置(我們可以通過 \(2\) 操作調整)。再思考一下,我們列舉每一個序列中每一個位置作為起點,其實就是求最長上升子序列的長度最大值 \(L\) ,然後答案就是 \(n-L\).(因為考慮操作 \(3\) 的效果就是把無序的元素變得有序,那我們找出最長的有序序列,那麼剩下的就是需要調整的,我們要使調整次數最少,就讓這個最長上升子序列最長)

​ 那麼這裡採用的方法是樹狀陣列優化DP,單調棧也可以。

​ 程式碼如下(馬蜂很醜,不喜勿噴)——

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define Tp template<typename T>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define maxn 1005
#define LL long long
using namespace std;
int n,ans=maxn,pos[maxn],maxx[maxn],p[maxn];
inline void add(int x,int y){for(register int i=x;i<=n;i+=i&-i) maxx[i]=max(maxx[i],y);}
inline int ask(int x){int res=0;for(register int i=x;i;i-=i&-i) res=max(res,maxx[i]);return res;}
class FileInputOutput
{
	private:
		static const int S=1<<21;
		#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
		#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch))
		char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[25];
	public:
		FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; }
		Tp inline void read(T& x)
		{
			x=0; char ch; while (!isdigit(ch=tc()));
			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
		}
		Tp inline void write(T x,const char& ch)
		{
			if (x<0) pc('-'),x=-x; RI ptop=0; while (pt[++ptop]=x%10,x/=10);
			while (ptop) pc(pt[ptop--]+48); pc(ch);
		}
		inline void flush(void)
		{
			fwrite(Fout,1,Ftop-Fout,stdout);
		}
		#undef tc
		#undef pc
}F;
int main(){
//	freopen("data.in","r",stdin);
	F.read(n);for(register int i=1;i<=n;i++) F.read(p[i]);for(register int i=n+1;i<=2*n;i++) p[i]=p[i-n];
	for(register int i=1;i<=n;i++){int j=i,sum=0;memset(maxx,0,sizeof(maxx));while(j!=i+n){int x=ask(p[j])+1;add(p[j],x);j++;sum=max(sum,x);}ans=min(ans,n-sum);}
	F.write(ans,'\n');return F.flush(),0;
}
//給定一個排列,每次可以把一個數取出插入到另一個位置,最小操作次數 -> 最長上升子序列