码迷,mamicode.com
首页 > 其他好文 > 详细

宝塔探险(树图专题)

时间:2017-10-02 09:33:29      阅读:383      评论:0      收藏:0      [点我收藏+]

标签:允许   dac   情况   abs   air   fps   代码实现   rom   dfa   

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时,务必注意初值!!!和方程;请动脑子打程序,不然会花去十倍的时间调试;这也是你为什么比其他人落后的原因,常规一样,初中也一样,不改必误你一生;

宝塔探险(树图专题)

标签:允许   dac   情况   abs   air   fps   代码实现   rom   dfa   

原文地址:http://www.cnblogs.com/AUSTIN-tkys/p/7618469.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!