1. 程式人生 > >bzoj 2824: [AHOI2012]鐵盤整理

bzoj 2824: [AHOI2012]鐵盤整理

題目描述

輸入輸出格式

輸入格式:

共兩行。第一行為鐵盤個數N(1<=N<=50),第二行為N個不同的正整數,分別為從上到下的鐵盤的半徑R。(1<=R<=100)

輸出格式:

一個正整數,表示使鐵盤從小到大有序需要的最少翻轉次數。

輸入輸出樣例

輸入樣例#1:
5
2 4 3 5 1
輸出樣例#1:
5
此題乍一看資料範圍極小,自然以為是水題,便想用bfs直接水過去,結果很滿意的得了10分,以下是暴力程式:
#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;
}
我發現是狀態太多的原因,於是想用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;
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*n次但都沒有解釋清楚,但身為蒟蒻的我一時半會兒想不通,以下就寫出證明過程以供其他蒟蒻參考:

每一次確定最末尾的那個數,步驟是第一步,把最末尾未確定的數換到第一個位置,接下來就可以換到最後了,接下來一樣的步驟,舉個例子:

對於 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天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的