1. 程式人生 > >寶塔探險(樹圖專題)

寶塔探險(樹圖專題)

過多 syn ecb sds 開始 bms floyd flow 多源

T1 升降梯上

題目描述

開啟了升降梯的動力之後,探險隊員們進入了升降梯運行的那條豎直的隧道,映入眼簾 的是一條直通塔頂的軌道、一輛停在軌道底部的電梯、和電梯內一桿控制電梯升降的巨大手 柄。 Nescafe 之塔一共有 N 層,升降梯在每層都有一個停靠點。手柄有 M 個控制槽,第 i 個控制槽旁邊標著一個數 Ci,滿足 C1<??0,表示手柄扳動到該槽 時,電梯將上升 Ci 層;如果 Ci<0,表示手柄扳動到該槽時,電梯將下降-Ci 層;並且一定 存在一個 Ci=0,手柄最初就位於此槽中。註意升降梯只能在 1~N 層間移動,因此扳動到使 升降梯移動到 1 層以下、N 層以上的控制槽是不允許的。 電梯每移動一層,需要花費 2 秒鐘時間,而手柄從一個控制槽扳到相鄰的槽,需要花費 1 秒鐘時間。探險隊員現在在 1 層,並且想盡快到達 N 層,他們想知道從 1 層到 N 層至少 需要多長時間? <??

<??輸入格式 第一行兩個正整數 N、M。 第二行 M 個整數 C1、C2??CM。 <??

<??輸出格式 輸出一個整數表示答案,即至少需要多長時間。若不可能到達輸出-1。 <??

<??樣例輸入 6 3 -1 0 2<??

<?? 樣例輸出 19 <??

<??樣例說明 手柄從第二個槽扳到第三個槽(0 扳到 2),用時 1 秒,電梯上升到 3 層,用時 4 秒。 手柄在第三個槽不動,電梯再上升到 5 層,用時 4 秒。 手柄扳動到第一個槽(2 扳到-1),用時 2 秒,電梯下降到 4 層,用時 2 秒。 手柄扳動到第三個槽(-1 扳倒 2),用時 2 秒,電梯上升到 6 層,用時 4 秒。 總用時為(1+4)+4+(2+2)+(2+4)=19 秒。<??

<?? 數據範圍與約定 對於 30% 的數據,滿足 1≤N≤10,2<=M<=5。 對於 100% 的數據,滿足 1≤N≤1000,2<=M<=20,-N<C1<C2<…<CM<N.<??

T2塔頂試探

題目描述

探險隊員們順利乘坐升降梯來到了塔頂。塔頂有若幹房間,房間之間連有通道。如果把 房間看做節點,通道看做邊的話,整個塔頂呈現一個有根樹結構,並且每個房間的墻壁都塗 有若幹種顏色的一種。 現在探險隊員們在樹根處,他們打算進一步了解塔頂的結構,為此,他們使用了一種特 殊設計的機器人。這種機器人會從隊員身邊,也就是樹根出發,之後對塔頂進行深度優先遍 歷。機器人每進入一個房間(無論是第一次進入還是返回),都會記錄這個房間的顏色。最 後,機器人會回到樹根。 顯然,機器人會訪問每個房間至少一次,並且穿越每條通道恰好兩次(兩個方向各一次), 然後,機器人會得到一個顏色序列。但是,探險隊員發現這個顏色序列並不能唯一確定塔頂 的結構。現在他們想請你幫助他們計算,對於一個給定的顏色序列,有多少種可能的結構會 得到這個序列。由於結果可能會非常大,你只需要輸出答案對 10^9 取模之後的值。 輸入格式 輸入文件包含一行,含有一個字符串,表示機器人得到的顏色序列。

輸出格式 輸出一個整數表示答案。

樣例輸入 ABABABA

樣例輸出 5

樣例說明 有如下 5 種方案。註意子樹之間是有序的,所以(3)和(4)是兩種不同的方案。

技術分享

數據範圍與約定 對於 24% 的數據,字符串的長度不超過 20。 對於 100% 的數據,字符串的長度不超過 300。

T3禮物運送

題目描述

機器人剛剛探查歸來,探險隊員們突然發現自己的腳下出現了一朵朵白雲,把他們托向 了空中。一陣飄飄然的感覺過後,隊員們發現自己被傳送到了一座空中花園。 “遠道而來的客人,我們是守護 Nescafe 之塔的精靈。如果你們想拜訪護法和聖主的話, 就要由我們引路。因此,你們是不是該給我們一點禮物呢 T_T?” 隊員們往遠處一看,發現花園中有 N 個木箱,M 條柔軟的羊毛小路,每條小路的兩個 端點都是木箱,並且通過它需要 ti 的時間。隊員們需要往每個木箱中放一份禮物,不過由 於精靈們不想讓花園被過多地踩踏,因此運送禮物的任務最多只能由兩位探險隊員完成。 兩位探險隊員同時從 1 號木箱出發,可以在任意木箱處結束運送。運送完畢時,每只木 箱應該被兩位隊員中至少一人訪問過。運送任務所用的時間是兩人中較慢的那個人結束運送 任務的時間。請問這個時間最快是多少呢?

輸入格式 第一行兩個正整數 N、M。 接下來 M 行每行三個整數 xi、yi、ti,表示 xi 和 yi 之間有一條用時為 ti 的小路,小路 是雙向的。

輸出格式 輸出所需的最短時間。

樣例輸入

6 6

1 2

10 2

3 10

3 4

5 4

5 10

5 6

20 2

5 10

樣例輸出

40

數據範圍與約定 對於 50%的數據,1<=N<=9。 對於 100%的數據,1<=N<=18,1<=ti<=1000,1<=xi,yi<=N,兩個木箱之間最多只有一 條小路,不會有自己到自己的小路,保證所有木箱是連通的。

昨天寢室裏開夜車寫作業的太多,燈亮如白晝。沒睡好,所以今天考試靈魂簡直在遊蕩,灌了大半杯咖啡後才好一些,最奇妙的是,我沒有做出簡單題T1,卻把比較有思維量的T2想出來了。。然後就寫掛了,處於爆零邊緣。。解就一起寫了,難免有些水,建立在大家都認真思考過的基礎上吧。

T1:

可能是我閱歷不夠,我首先想到的是dp(不再贅述),但是發現dp有後效性,而且記憶化會死循環(因為有負數),我那時就突然想到了建圖。然後我們考慮對層數i建圖,但是根本不行,你不能直接在節點i上加邊,因為在你的控制槽位置不確定的情況下,你無法確定邊的大小。所以就可以將一個二元組看成一個點。即(i,k),考慮非常顯然的(i,k)到(i+a[j],j)的邊為abs(k-j)+2*abs(a[j]);有這樣一個圖之後,你從起點(1,k0)到終點(n,j),j=1~m,a[k0]=0的;為什麽能,你可以把最短路看成更廣的具後效性的dp,用你的建圖直覺和dp直覺去理解。

到這裏還遠遠不夠,這個題比較煩的應該是代碼實現,(如果你是初次遇到這個題的話),仔細看。註意下面幾點:

1.如何將二元組建在一起 2.有沒有必要建圖 (這裏只專門用數據來存)

技術分享
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <queue>
 7 #define N 1010
 8 #define M 210
 9 #define inf 0x3f3f3f3f
10 #define Run(i,l,r) for(int i=l;i<=r;i++)
11 #define Don(i,l,r) for(int i=l;i>=r;i--)
12 using namespace std;
13 struct node{int i,j; bool operator < (const node& a)const{return i<a.i;} };
14 typedef pair<int,node>pii;
15 priority_queue<pii,vector<pii>,greater<pii> >q;
16 int vis[N][M],dis[N][M]; 
17 int n,m,a[M];
18 int main()
19 {    freopen("updown.in","r",stdin);
20     freopen("updown.out","w",stdout);
21     cin>>n>>m;
22     int sj;
23     Run(i,1,m){
24         cin>>a[i];
25         if (!a[i]) sj=i;
26     }
27     memset(dis,0x3f,sizeof(dis));
28     dis[1][sj]=0;
29     q.push(make_pair(dis[1][sj],(node){1,sj}));
30     vis[1][sj]=1;
31     while (!q.empty())
32     {node  u=q.top().second;
33     q.pop(); vis[u.i][u.j]=0;
34     for(int j=1;j<=m;j++)
35     {node v=(node){u.i+a[j],j};
36     if (v.i<1||v.i>n) continue;
37     if (dis[v.i][v.j]>dis[u.i][u.j]+abs(j-u.j)+2*abs(a[j]))
38        {dis[v.i][v.j]=dis[u.i][u.j]+abs(j-u.j)+2*abs(a[j]);
39     if (!vis[v.i][v.j]) {q.push(make_pair(dis[v.i][v.j],v));
40                         vis[v.i][v.j]=1;
41                         }}
42     } 
43     
44     }
45     int ans=0x3f3f3f3f;
46     Run(i,1,m) ans=min(ans,dis[n][i]);    
47     cout<<((ans==inf)?-1:ans)<<endl;
48     return 0;
49 }
View Code

T2:

這個題就是我唯一寫了但是寫掛了的QAQ,它其實是個區間dp。我開始也從一般的dp去考慮:

不分析細節,那麽就有這樣一件事情,在同一個串(例ABABABA)裏狡猾的A顏色(除第一個外和最後一個),有可能是dfs回來的根節點,也有可能是

在是[l,r]中s[l]==s[r]時,考慮s為AS1AS2A…….A的形式Sn表示一個字符串集,即技術分享

如果我們要專門去討論s1到sn的話,還要生成子集,代碼復雜性提高了不說,時間也過不了。而我們本身寫的就是dp,所以可以利用這個優勢,在劃分[l,r]的手段上變化:具體就是我們不關心s2到sn,只枚舉s1,然後將AS2AS2AS3..SNA原樣(就變成了一個更小的子問題)dp而將s1dp即S1,(s1無頭尾的A),至於l還是從r枚舉s1,就是隨便了。寫出轉移方程:

dp[l,r]=sigma(dp[l+1,k]*dp[k,r]) (s[l]==s[r]&&s[l]==s[k] ).

技術分享
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring> 
 5 #define N 310
 6 #define inf 0x3f3f3f3f
 7 #define Mod 1000000001
 8 #define Run(i,l,r) for(int i=l;i<=r;i++)
 9 #define Don(i,l,r) for(int i=l;i>=r;i--)
10 using namespace std;
11 char s[N];
12 int f[N][N];
13 int main()
14 {    
15     freopen("probe.in","r",stdin);
16     freopen("probe.out","w",stdout);
17     cin>>s;
18     int len=strlen(s)-1;
19     Run(i,0,len) f[i][1]=1;
20     
21     
22     Run(l,3,len+1)
23     for(int i=0;i+l-1<=len;i++)
24     {int j=i+l-1;
25     if (s[i]==s[j]){
26         Don(k,j,i+2) 
27         if (s[i]==s[k]) f[i][l]=(f[i][l]+1ll*f[i+1][k-i-1]*f[k][j-k+1]%Mod)%Mod;
28     }
29     }
30     cout<<f[0][len+1]<<endl;
31     return 0;
32 }
View Code

T3

先看數據範圍可以猜出是狀壓dp,然而他們是兩個人一起走,而又沒說不能走相同的地方之類的,所以我們不妨將此題看為一個人走。如果不這樣,設計狀態是就要三維,分別為1號人最後走到的地方i,2號人最後走到的地方j,和此時兩人一共走過的集合s

(二進制狀壓),你悄悄算算就會發現一件很奇妙的事情:MLE。所以我們只能考慮記錄一個i,s所以得到的應該是對於這個圖的每一種遍歷過的點集s,走到的末位置為i的情況下的最少步數。考慮兩個人無論誰走都是一樣的,那麽我們想要得到答案就應該是min{max(f[s|1,i],f[((1<<n)-1-s)|1,j])} (ij∈V);(之所以要|1是因為1是出發點,其他點可以取反,但1不能)

所以是這樣的嗎?如果你照著這個思路用普通的狀壓dp擴展你會發現你連樣例都過不了。因為樣例中12還共同走了一段而這一段是必須的。如何把這一段加進去呢——其實,跑最短路將連通圖的每兩個點的最短距離算出來即可,因為在運用最短路擴展時,計算dis[][]數組會加進一些必要經過的點並且這些點是最優的,所以現在我們完善狀態:一定會經過s中點,但經過的點不一定是S(子集關系)應為有可能多經過其他點,而更優。那所以到這裏狀態轉移方程才是正確的。建議用floyd求多源點最短路。

技術分享
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <queue>
 5 #include <algorithm>
 6 #include <vector>
 7 #include <cmath>
 8 #include <ctime>
 9 #define N 20
10 #define inf 0x3f3f3f3f
11 #define Run(i,l,r) for(int i=l;i<=r;i++)
12 #define Don(i,l,r) for(int i=l;i>=r;i--)
13 using namespace std;
14 int n,m,dis[N][N];
15 int f[1<<N][N];
16 int main()
17 {    
18     freopen("transport.in","r",stdin);
19     freopen("transport.out","w",stdout);
20     cin>>n>>m;
21     memset(dis,0x3f,sizeof(dis));
22     Run(i,1,m)
23     {int u,v,w;
24     cin>>u>>v>>w;
25     dis[u][v]=dis[v][u]=w;    
26     }
27     Run(i,1,n) dis[i][i]=0; 
28     Run(k,1,n)Run(i,1,n)Run(j,1,n) if(dis[i][k]+dis[k][j]<dis[i][j]) dis[i][j]=dis[i][k]+dis[k][j];
29     memset(f,0x3f,sizeof(f));
30     
31     f[1][1]=0;
32     Run(i,1,(1<<n)-1) Run(j,1,n)
33     if ((1<<j-1)&i)
34     Run(k,1,n)if (((1<<k-1)&(i))&&f[i][j]>f[i-(1<<j-1)][k]+dis[k][j]&&i!=j) f[i][j]=f[i-(1<<j-1)][k]+dis[k][j];
35     
36     Run(i,1,(1<<n)-1)
37     Run(j,1,n)
38     f[i][0]=min(f[i][0],f[i][j]);
39     int ans=inf;
40     Run(i,1,(1<<n)-1)
41     {ans=min(ans,max(f[i|1][0],f[((1<<n)-1-i)|1][0]));
42     }
43     Run(i,1,(1<<n)-1) cout<<f[i][0]<<" ";
44     cout<<endl;
45     cout<<ans<<endl;
46     return 0;
47 } 
View Code

總結:

1.區間DP的l要放在最外邊;
3.註意spfa可以用priotity優化,然後加入隊列的條件是v不在隊列裏且被更新了;
判環的方法就是更新次數超過n-1次;
3.flyod的k應該在最外面(先記著,有感性的理解,說不定那天有靈感就相出來了)
4.寫dp時,務必註意初值!!!和方程;請動腦子打程序,不然會花去十倍的時間調試;這也是你為什麽比其他人落後的原因,常規一樣,初中也一樣,不改必誤你一生;

寶塔探險(樹圖專題)