1. 程式人生 > >Jzoj5441【NOIP2017提高A組衝刺11.1】序列

Jzoj5441【NOIP2017提高A組衝刺11.1】序列

給定一個1~n的排列x,每次你可以將x1~xi翻轉。你需要求出將序列變為升序的最小操作次數。有多組資料。

此題十分不友善

對於多年沒有打過搜尋的蒟蒻更是如此

(強行)假定這個題資料範圍是在坑人(因為我以前真的見過有人二分圖匹配的題正解是狀壓DP的)

開始尋找多項式演算法。。。。。

發現各種貪心都不行。。。。。

實在不行開始寫暴力,最後掛掉0分

說說正解:迭代加深+剪枝

我們考慮兩個東西,深度上限:2n-2 這個非常顯然(其實還有一種說法是上界為n但是沒有人證明)

估價函式f(S),表示S到結果至少需要多少步

我們令 f(S)=Σ[abs(S[i]-S[i+1])>1]+已用的步數和步數上限做比較如果大於直接退出

實現f非常簡單,我們考慮為什麼這樣是對的

從各種貪心和樣例來看,我們發現若干個連續的數字在最優解中肯定不會被破壞(誤,其實我也不知道為什麼)

引用題解:

我們發現每次翻轉只會改變一對相鄰數對,因此對於一個狀態求出相差>1 的相鄰數對的數量,剩餘步數一定大於這個值。加上這個剪枝就能通過本題。

假裝這個是顯然的,我們就可以迭代加深了

700ms+,十分菜

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[50],b[50],n,m;
bool
dfs(int p,int d){ if(d+p>m) return 0; for(int i=1;i<=n;++i) if(a[i]!=i) goto next; return 1; next: for(int q,i=2;i<=n;++i){ q=d+(i<n&&abs(a[i]-a[i+1])==1)-(i<n&&abs(a[1]-a[i+1])==1); reverse(a+1,a+i+1); if(dfs(p+1,q)) return 1; reverse(a+1,a+i+1); } return
0; } int aim(){ int p=0; scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",a+i); for(int i=1;i<n;++i) if(abs(a[i]-a[i+1])>1) ++p; int l=0,r=n<<1; memcpy(b,a,n+3<<2); for(;l<r;){ m=l+r>>1; if(dfs(0,p)) r=m; else l=m+1; memcpy(a,b,n+3<<2); } printf("%d\n",l); } int main(){ freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); int T; for(scanf("%d",&T);T--;aim()); }