bzoj 2824: [AHOI2012]鐵盤整理
題目描述
輸入輸出格式
輸入格式:共兩行。第一行為鐵盤個數N(1<=N<=50),第二行為N個不同的正整數,分別為從上到下的鐵盤的半徑R。(1<=R<=100)
輸出格式:一個正整數,表示使鐵盤從小到大有序需要的最少翻轉次數。
輸入輸出樣例
輸入樣例#1:5 2 4 3 5 1輸出樣例#1:
5此題乍一看資料範圍極小,自然以為是水題,便想用bfs直接水過去,結果很滿意的得了10分,以下是暴力程式:
我發現是狀態太多的原因,於是想用trie判重,但是陣列小一點便re,大一點就mle,還是十分,僅貼一個程式作為紀念:#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define inf 999999999 #define For(i,a,b) for(i=a;i<=b;++i) #define rep(i,a,b) for(i=a;i>=b;--i) #define mm(a,b) memset(a,b,sizeof(a)) #define ll long long using namespace std; int read(){ int sum=0,flag=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();} while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar(); return sum*flag; } int maxx(int x,int y){ if(x<y)return y; return x; } int minn(int x,int y){ if(x<y)return x; return y; } int abss(int x){ if(x>=0)return x; return -x; } const int maxn = 100010; int a[51],b[51]; struct node{ int s[51],t; }; int n,cnt; node tmp; void bfs(){ int i,j; node T; memcpy(T.s,a,sizeof(a)); T.t=0; queue<node>q; q.push(T); while(!q.empty()){ T=q.front(); q.pop(); int jicun=n; while(T.s[jicun]==b[jicun])jicun--; For(i,2,jicun){ memcpy(tmp.s,T.s,sizeof(T.s)); int k=i>>1; For(j,1,k){//直接翻轉 int ch=tmp.s[j]; tmp.s[j]=tmp.s[i-j+1]; tmp.s[i-j+1]=ch; } tmp.t=T.t+1; if(memcmp(tmp.s,b,sizeof(b))==0){//如果到達目標狀態則結束 printf("%d\n",T.t+1); return ; } q.push(tmp); } } } int main(){ int i,j; n=read(); For(i,1,n){ a[i]=read(); b[i]=a[i]; } sort(b+1,b+n+1); if(memcmp(a,b,sizeof(b))==0){ printf("1\n"); return 0; } bfs(); return 0; }
我開始認真對待這道題,一些題解裡說最多可以只翻2*n次但都沒有解釋清楚,但身為蒟蒻的我一時半會兒想不通,以下就寫出證明過程以供其他蒟蒻參考:#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define inf 999999999 #define For(i,a,b) for(i=a;i<=b;++i) #define rep(i,a,b) for(i=a;i>=b;--i) #define mm(a,b) memset(a,b,sizeof(a)) #define ll long long using namespace std; int read(){ int sum=0,flag=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();} while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar(); return sum*flag; } int maxx(int x,int y){ if(x<y)return y; return x; } int minn(int x,int y){ if(x<y)return x; return y; } int abss(int x){ if(x>=0)return x; return -x; } const int maxn = 100010; int a[51],b[51]; struct node{ int s[51],t; }; int n,cnt; int trie[2000000][51]; node tmp; int pd(){//字典樹部分 int i,u=0,flag=0; For(i,1,n){ if(!trie[u][tmp.s[i]]){ flag=1; trie[u][tmp.s[i]]=++cnt; } u=trie[u][tmp.s[i]]; } if(flag)return 1; return 0; } void bfs(){ int i,j; node T; memcpy(T.s,a,sizeof(a)); T.t=0; queue<node>q; q.push(T); while(!q.empty()){ T=q.front(); q.pop(); For(i,2,n){ memcpy(tmp.s,T.s,sizeof(T.s)); int k=i>>1; For(j,1,k){ int ch=tmp.s[j]; tmp.s[j]=tmp.s[i-j+1]; tmp.s[i-j+1]=ch; } if(pd()){//判重 tmp.t=T.t+1; if(memcmp(tmp.s,b,sizeof(b))==0){ printf("%d\n",T.t+1); return ; } q.push(tmp); } } } } int main(){ int i,j; n=read(); For(i,1,n){ a[i]=read(); b[i]=a[i]; } sort(b+1,b+n+1); if(memcmp(a,b,sizeof(b))==0){ printf("1\n"); return 0; } bfs(); return 0; }
每一次確定最末尾的那個數,步驟是第一步,把最末尾未確定的數換到第一個位置,接下來就可以換到最後了,接下來一樣的步驟,舉個例子:
對於 2 4 3 5 1,最開始要確定最後一位,也就是要把最後一位變成5,於是先把5放到第一位,->5 3 4 2 1,再換到最後1 2 4 3 5,之後5就不管他了
只剩下1 2 4 3,於是一樣的->4 2 1 3->3 1 2 4,還有3個數,3 1 2->2 1 3->1 2 3於是就排好了,算下來,最壞情況只需要2*n-1步
於是就有了第一個剪枝(大於2*n-1的情況可以直接減掉),然而有了這個剪枝還是沒有什麼用,我們還要利用另外一個原理:若兩個數在目前位置上相鄰,但在目標位置上不相鄰,則因為是翻轉,因此至少需要一步把這兩個數分開,因此從當前排列到目標排列最少就需要出現這種情況的總和。待會程式碼裡還會解釋:
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
int sum=0,flag=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
return sum*flag;
}
int maxx(int x,int y){
if(x<y)return y;
return x;
}
int minn(int x,int y){
if(x<y)return x;
return y;
}
int abss(int x){
if(x>=0)return x;
return -x;
}
const int maxn = 100010;
struct node{
int value,pos,nowpos;
};
node a[maxn];
bool cmp(node c,node d){
return c.value<d.value;
}
int ans,n;
void dfs(int sum,int total){//sum是已走過的步數,total是當前排列到達目標狀態的預計步數
if(!total&&a[1].nowpos==1){ans=minn(ans,sum);return;}//若目標步數為0,且第一個位置上是對的,這是為了預防如5 4 3 2 1這種情況的出現
int i,j;
For(i,2,n){//列舉翻前i位
int zong=total;
if(i<n){//如果i位置與i+1位置相鄰且預計位置不相同,則可以通過這步操作分開他們,同時預計步數減少,但要考慮換過來後第1個數和第i+1個數的位置關係
zong-=(abss(a[i].nowpos-a[i+1].nowpos)!=1)-(abss(a[1].nowpos-a[i+1].nowpos)!=1);
}
if(zong+sum<ans){//若當前步數加上預計步數大於已搜到的,就剪掉
int mid=i/2;
For(j,1,mid){
int tmp=a[j].nowpos;
a[j].nowpos=a[i-j+1].nowpos;
a[i-j+1].nowpos=tmp;
}
dfs(sum+1,zong);
For(j,1,mid){//回溯
int tmp=a[j].nowpos;
a[j].nowpos=a[i-j+1].nowpos;
a[i-j+1].nowpos=tmp;
}
}
}
}
int main(){
int i,j;
n=read();
For(i,1,n){
a[i].value=read();
a[i].pos=i;
}
sort(a+1,a+n+1,cmp);
For(i,1,n){
a[a[i].pos].nowpos=i;//把當前位置的目標位置記錄
}
int sum=0;
ans=n*2-1;//至多搜n*2-1次
For(i,2,n){
sum+=abss(a[i].nowpos-a[i-1].nowpos)!=1;//如果當前排列中位置相鄰但目標排列中並不相鄰則預計步數加1
}
dfs(0,sum);
printf("%d\n",ans);
return 0;
}
剪枝萬歲!相關推薦
bzoj 2824: [AHOI2012]鐵盤整理
題目描述 輸入輸出格式 輸入格式:共兩行。第一行為鐵盤個數N(1<=N<=50),第二行為N個不同的正整數,分別為從上到下的鐵盤的半徑R。(1<=R<=100) 輸出格式:一個正整數,表示使鐵盤從小到大有序需要的最少翻轉次數。 輸入輸出樣例
洛谷 P2534 [AHOI2012]鐵盤整理
targe ans -m 一行 content scanf 有序 pid flag P2534 [AHOI2012]鐵盤整理 題目描述 輸入輸出格式 輸入格式: 共兩行。第一行為鐵盤個數N(1<=N<=50),第二行為N個不
【硬盤整理】使用UltimateDefrag將常用文件放置在磁盤最外圈
16px pan def -s 硬盤 .com src style page 使用方法未知。軟件截圖如下: 官方網站(英文):http://www.disktrix.com/ 漢化破解版V3.0下載地址:http://page2.dfpan.com/fs/7com9mon
服務器磁盤空間擴容整理 pv vg lv resize2fs
linux 服務器 磁盤空間 溫馨提示:PV(physical volume)即物理卷,就是物理磁盤,可以通過fdisk -l 查看操作系統有幾塊硬盤VG(volume group)即卷組,就是一組物理磁盤的組合,裏面可以有一塊硬盤也可以有多塊硬盤LV(logical volume)及邏輯卷,就是
【BZOJ 2822】[AHOI2012]樹屋階梯 卡特蘭數+高精
div cnblogs operator line code clu while pan .... 這道題隨便弄幾個數就發現是卡特蘭數然而為什麽是呢? 我們發現我們在增加一列時,如果這一個東西(那一列)他就一格,那麽就是上一次的方案數,並沒有任何改變,他占滿了也是,然後他
BZOJ[1043] 下落的圓盤
下落的圓盤 cstring main cout esp init void i++ ring /*cmath庫*/ 1 #include <cmath> 2 #include <cstdio> 3 #include <cstring&
BZOJ 3168 [Heoi2013]鈣鐵鋅硒維生素
跟著 algorithm 一次 貪心 printf 字符 只需要 std 之間 Description 銀河隊選手名單出來了!小林,作為特聘的營養師,將負責銀河隊選手參加宇宙比賽的飲食。眾所周知,前往宇宙的某個星球,通常要花費好長好長的時間,人體情況在這之間會發生變化,因此
硬盤知識點學習整理
作用 memory 存在 ron 成本 測試 dram ddr ash 一、固態硬盤(SSD) -組成 -閃存芯片 -主控芯片 -緩存芯片:部分廉價的固態硬盤為了節約成本會省去緩存芯片 -接口:同樣配置不同接口會影響性能,主流是SATS接口 -存貯介質 -一
Linux磁盤管理的基本知識簡單整理
int oot mkfs.ext4 擴展分區 gdisk 記錄 操作 mount 人在 磁盤基本知識 固態硬盤的優缺點 IDE:hda hdb hdc SCISI:sda sdb sdc MBR與GPT 存放分區表的一種形式MBR:Master Boot Record 主
win10磁盤碎片整理
性能 .com 我們 使用 height image 箭頭 驅動器 卡頓 如果我們想要加快win10系統運行速度的話,就需要定期整理碎片才可以,減少卡頓,提高性能。 一:註意事項 固態硬盤用戶千萬不要使用‘磁盤碎片整理功能’,因為使用的技術不一樣,使用window自帶的該功
安卓入門到進階推薦書籍整理pdf附網盤連結已拿阿里豆瓣offer(珍藏)
轉載自某大佬部落格:https://pymlovelyq.github.io/2018/09/04/An/ 前言:技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好
python從入門到進階推薦書籍最全整理pdf分享附網盤連結已拿BT豆瓣offer
從八月底開始找工作,短短的一星期多一些,面試了9家公司,拿到5份Offer,可能是因為我所面試的公司都是些創業性的公司吧,不過還是感觸良多,因為學習Python的時間還很短,沒想到還算比較容易的找到了工作,就把自己找的python資源和大家分享一下,希望為學習Python找工作的小夥
學習資料庫Mysql/Oracle/SQL從入門到進階書籍pdf版吐血整理推薦附網盤連結(珍藏版)
轉載自某大佬部落格:https://pymlovelyq.github.io/2018/10/12/database/ 前言:技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結
自己就能組私人云盤?16T紅盤+鐵威馬F4-221 搞定!
前幾天我寫過一篇關於硬碟櫃+路由器組建模擬NAS的文章,那其實只是沒有辦法的辦法,誰讓咱鍋裡沒米呢?現在發工資了,硬氣了,我讓你們看看真正的NAS:鐵威馬F4-221網路儲存伺服器。同時我還配了2塊8TB的西數紅盤組RAID。 先看什麼是NAS?是一種專用資料儲存伺服器,以資料為中心。NA
面對小米路由器再次崩盤,入手鐵威馬TNAS+西數8T有感!
前端時間家裡的智慧路由器再次罷工,已經記不起這是第幾次了,對此除了無奈就是深深的失望,儲存在1T硬碟中的各項資料灰飛煙滅。電腦裝置更新迭代的速度太快,且安全性不高,難以成為家庭主打儲存,一款具備高速、安全、多硬碟、遠端訪問、手機備份擴容、遠端下載的NAS無疑是主流智慧家庭的必需品。 筆者將
安卓程式設計師推薦書單從入門到進階整理pdf已拿阿里豆瓣offer(附網盤連結)
轉載自某大佬部落格:https://pymlovelyq.github.io/2018/09/04/An/ 前言:技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好
【BZOJ】3168: [Heoi2013]鈣鐵鋅硒維生素
題解 Ca Fe Zn Se 顯然我們既然初始矩陣就能通過線性變換變成單位矩陣,則該矩陣一定有逆 沒有逆輸出NIE 而且因為這些向量兩兩正交,則表示一個向量的時候表示方法唯一 那麼我們求一個逆可以求出這個矩陣消成單位矩陣的線性表示,再拿第二個矩陣和逆矩陣相乘可以得到第二個矩陣每個行向量用第
鐵威馬NAS如何下載百度雲盤的檔案?
百度雲盤同步是一款檔案同步應用程式。百度雲盤同步可實現將TNAS 中的檔案與百度雲盤同步的功能。 1.請前往鐵威馬TOS “應用中心”, 安裝百度雲盤同步; 2.在TOS 桌面,雙擊百度雲盤同步開啟應用程式; 3.輸入百度雲盤賬號及密碼,點選登入; 4.選
python從入門到進階推薦書籍史上最全整理pdf分享網盤下載附連結
前言: 技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。 對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定
演算法與資料結構+一點點ACM從入門到進階吐血整理推薦書單pdf附網盤下載連結
前言: 技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的