Jzoj5441【NOIP2017提高A組衝刺11.1】序列
阿新 • • 發佈:2019-01-10
給定一個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());
}