T3:BFS

回看了一下Day1的T3...感觉裸裸的BFS,自己当时居然没有看出来...

同时用上升和下降两种状态bfs即可

这一题还要注意一个细节的地方,就是题目要求的是求往返的最优解

k=min(d[上升],d[下降]);

ans=min(2*k+1,d1[]+d2[]);

输出ans..这个地方需要理解;

以及如果这个点在边缘地区(边界) 且这个点的高度为0 那么就不需要转化

其余的照bfs模板打即可:

//刚开始以为是把每一个点都bfs一遍,看了标程之后发现有一个优很多的解法:

把出发点定为(0,0),设想在山区外面还有一圈海拔为极大值(或0)的区域

根据bfs算法的思路,可想从(0,0)出发之后当然为最优解

这样只需要bfs一遍就可以了(上升和下降两种状态同时),时间上省了很多;

//mark;感谢ccy大神详细的注释

直接附上标程:

  1. //感谢笑尘的思路
  2. const
  3. dx:array[1..4] of longint=(0,0,1,-1);
  4. dy:array[1..4] of longint=(1,-1,0,0);//四个方向
  5. var
  6. h:array[1..1000000] of record
  7. //队列 x,y表示坐标 g中1表示上升状态,2表示下降状态 l表示用掉几个装置
  8. x,y:longint;
  9. g:1..2;
  10. l:longint;
  11. end;
  12. dis:array[-1..105,-1..105,1..2] of longint;//每个坐标的最优解(用掉几个装置)
  13. a:array[-1..105,-1..105] of longint;//存地图
  14. n,m,i,j,head,tail,x1,y1,x2,y2,p,cishu,k:longint;
  15.  
  16. function min(a,b:longint):longint;//返回较小值
  17. begin
  18. if a>b then exit(b) else exit(a);
  19. end;
  20.  
  21. begin
  22. fillchar(a,sizeof(a),0);
  23. fillchar(dis,sizeof(dis),$7f div 3);//初始化值要大
  24. readln(m,n);
  25. for i:=1 to m do
  26. begin
  27. for j:=1 to n do
  28. read(a[i,j]);
  29. readln;
  30. end;
  31. head:=0;tail:=2;
  32. h[1].x:=0;h[1].y:=0;h[1].g:=1;h[1].l:=0;//这是第一个是上升状态的情况
  33. h[2].x:=0;h[2].y:=0;h[2].g:=2;h[2].l:=0;//这是第一个是下降状态的情况
  34. dis[0,0,1]:=0;//最优解一开始(0,0)坐标都是0
  35. dis[0,0,2]:=0;
  36. while head<tail do
  37. begin
  38. inc(head);
  39. x1:=h[head].x;y1:=h[head].y;p:=h[head].g;cishu:=h[head].l;//x1,y1是坐标(从队列中取出),p是当前状态,cishu是转//换次数
  40. for i:=1 to 4 do
  41. begin
  42. x2:=x1+dx[i];
  43. y2:=y1+dy[i];
  44. if (x2>=0) and (x2<=m+1) and (y2>=0) and (y2<=n+1) then//如果这四个方向符合
  45. begin
  46. if (a[x2,y2]>=a[x1,y1]) and (p=1) and (dis[x2,y2,1]>cishu) then
  47. //如果目标点的高度大于等于当前的点的高度,而且当前处于上升阶段 那么cishu不需要加1,直接和目标点的最优解比较。如果可//以更新则更新
  48. begin
  49. dis[x2,y2,1]:=cishu;//更新dis节点
  50. inc(tail);
  51. h[tail].x:=x2;
  52. h[tail].y:=y2;
  53. h[tail].g:=1;
  54. h[tail].l:=cishu;
  55. end;
  56. if (a[x2,y2]>a[x1,y1]) and (p=2) and (dis[x2,y2,1]>cishu+1) then
  57. //如果目标点的高度大于当前的点的高度且处于下降状态 那么cishu+1,用cishu+1和目标点的最优解比较
  58. begin
  59. dis[x2,y2,1]:=cishu+1;//更新dis节点
  60. inc(tail);
  61. h[tail].x:=x2;
  62. h[tail].y:=y2;
  63. h[tail].g:=1;
  64. h[tail].l:=cishu+1;
  65. end;
  66. if (a[x2,y2]<a[x1,y1]) and (p=1) and (dis[x2,y2,2]>cishu+1) then
  67. //如果目标点的高度小于当前的点,且当前处于上升状态 那么cishu+1,用cishu+1和目标点的最优解比较
  68. begin
  69. dis[x2,y2,2]:=cishu+1;//更新dis节点
  70. inc(tail);
  71. h[tail].x:=x2;
  72. h[tail].y:=y2;
  73. h[tail].g:=2;
  74. h[tail].l:=cishu+1;
  75. end;
  76. if (a[x2,y2]<=a[x1,y1]) and (p=2) and (dis[x2,y2,2]>cishu) then
  77. //如果目标点的高度小于或等于当前的点,且当前处于下降状态 那么cishu不需要+1,用cishu和目标点的最优解比较
  78. begin
  79. dis[x2,y2,2]:=cishu;//更新dis节点
  80. inc(tail);
  81. h[tail].x:=x2;
  82. h[tail].y:=y2;
  83. h[tail].g:=2;
  84. h[tail].l:=cishu;
  85. end;
  86. end;
  87. end;
  88. end;//直到队列结束
  89. for i:=1 to m do
  90. begin
  91. for j:=1 to n do
  92. begin
  93. k:=min(dis[i,j,1],dis[i,j,2]);//先找出dis中的上升状态的最优解和下降状态的最优解 取最小值
  94. if (k=0) and (a[i,j]=0) and ((i=1) or (i=m) or (j=1) or (j=n)) then
  95. if j=1 then write(0) else write(' 0')//如果这个点在边缘地区(边界) 且这个点的高度为0 那么就不需要转化装//置
  96. else if j=1 then write(min(2*k+1,dis[i,j,1]+dis[i,j,2])) else//其他情况下在2*k+1和dis[i,j,1]+dis//[i,j,2]中求更优解,这两个加起来表示其不用相同的方式回去。
  97. write(' ',min(2*k+1,dis[i,j,1]+dis[i,j,2]));//之所以判断j=1是为了防止末尾输出多余空格
  98. end;
  99. writeln;
  100. end;
  101. end.

//分别讨论四种情况,并把符合的全部入队

理解了思路打起来并不难,主要是要细心和耐心;

T4:树形DP;

由于自己在长乐集训那一段时间没有很认真地静下心来理解树形DP,以至于对树形DP没有什么概念

回来自己补题解的时候,认真看了某位学长的题解(貌似这天是师宜写的?),以及ccy大神的注释,觉得其实也很好理解

跟老人家俊(?还是萌哲?)说的很像吧,无非就是在树上的DP

用邻接表存边,然后根据父亲和子树的关系写DP

DP主要是由三个数组实现:

f1表示父亲这个点放人看守 并且这个点已经被控制到了,即有人监视着这台电脑.
f2表示父亲这个点不放人看守,但是这个点已经被控制到了.
f3表示父亲这个点没有人控制到,但是其子树全部都被控制到了。

然后状态转移方程就是:

f1[x]:=f1[x]+min(min(f1[go[e]],f2[go[e]]),f3[go[e]]);

//这个点之所以三种状态都能是因为,这个点要放了,那么其儿子节点也可以选择放或则不放,其较优解,//对于这个儿子节点没有被控制的情况,如果这个点放了,那么这个儿子节点就可以被控制了
          f3[x]:=f3[x]+f2[go[e]];//f3的意义是这个点没有被控制,但是子树都被控制了,这和2的含义正好相对应所以加上它就是累加f3的值
          if f1[go[e]]<=f2[go[e]] then bo:=true;//如果放更优那么bo就为true 否则就保持不变为false;(可以想象如果不放更优,那么就会出现这个父亲节点没人控制的情//况)
          f2[x]:=f2[x]+min(f1[go[e]],f2[go[e]]);//不放的话就不能加上第三种情况了,因为那样就会有一个点不受控制
          aa:=min(aa,f1[go[e]]-f2[go[e]]);//这是为了防止上述 “不放更优”的情况出现,那样我们必须在儿子中找一个“较优值"来控制这个父亲;

如果忘了找aa的原因,可以看学长的注释:

  1. 如果父亲放了人,那么孩子就不需要放人了,当然也可以放人,所以可以直接取它们的最小值。如果父亲不放人,孩子至少要有一个放人,否则就看守不住父亲。所以,此时要找一个最优的孩子u来放人。怎么找到u呢?
  2.  
  3. 我们可以这样考虑:穷举所有的孩子节点,用D[i]表示孩子节点iDP[I,1]-DP[I,0]的值。这个值越小,说明这个节点放人越好。于是找到所有孩子中这个值的最小值,把这时的i作为u节点使用。

理解了过程写起来其实很简单:

自己乱yy了一下

//是把控制孩子的人数推回去,以及要不要加上u指也是根据孩子的f1,f2

这里的孩子和父亲的关系,是相对的

至于递归的时候是check(1,0)中的1指的是从邻接表的第一个节点开始

最后比较f1[1],f2[1]即可.

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. using namespace std;
  5. int f1[1500],f2[1500],f3[1500];
  6. int tot=0,u,n,x,k,y;
  7. struct node{
  8. int from,to,next;
  9. }a[1500];
  10. void add(int x,int y){
  11. a[++tot].next=a[x].from;
  12. a[x].from=tot;
  13. a[tot].to=y;
  14. }
  15. void check(int x,int fa){
  16. u=1500;
  17. bool f=false;
  18. int e=a[x].from;
  19. while(e!=0){
  20. if(a[e].to!=fa){
  21. check(a[e].to,x);
  22. f1[x]=f1[x]+min(min(f1[a[e].to],f2[a[e].to]),f3[a[e].to]);
  23. f3[x]=f3[x]+f2[a[e].to];
  24. if(f1[a[e].to]>f2[a[e].to]) f=true;
  25. f2[x]=f2[x]+min(f1[a[e].to],f2[a[e].to]);
  26. u=min(u,f1[a[e].to]-f2[a[e].to]);
  27. }
  28. e=a[e].next;
  29. }
  30. f1[x]++;
  31. if(f) f2[x]=f2[x]+u;
  32. }
  33. int main(){
  34. scanf("%d",&n);
  35. for(int i=1;i<=n;i++){
  36. scanf("%d%d",&x,&k);
  37. for(int j=1;j<=k;j++){
  38. scanf("%d",&y);
  39. add(x+1,y+1);
  40. add(y+1,x+1);
  41. }
  42. }
  43. check(1,0);
  44. printf("%d",min(f1[1],f2[1]));
  45. return 0;
  46. }

顺便附上ccy大神的各种详细注释:

  1. 这个程序:(可以看看我画的示意图)
  2. 采用邻接表的形式存储边信息(因为是无向图所以要多存一条);
  3.  
  4. f1表示父亲这个点放人看守 并且这个点已经被控制到了,即有人监视着这台电脑.
  5. f2表示父亲这个点不放人看守,但是这个点已经被控制到了.
  6. f3表示父亲这个点没有人控制到,但是其子树全部都被控制到了。
  7.  
  8. var
  9. i,j,n,x,k,y,tot:longint;
  10. f1,f2,f3:array[0..2000] of longint;
  11. first,next,go:array[0..10010] of longint;
  12.  
  13. function min(a,b:longint):longint;
  14. begin
  15. if a>b then exit(b) else
  16. exit(a);
  17. end;
  18.  
  19. procedure cm(x,fa:longint);//x的父亲是fa
  20. var
  21. bo:boolean;
  22. e,aa:longint;
  23.  
  24. begin
  25. aa:=10000;
  26. bo:=false;
  27. e:=first[x];//找到表头
  28. while e<>0 do//如果还有出度
  29. begin
  30. if go[e]<>fa then//如果这个点的出度不是父亲节点
  31. begin
  32. cm(go[e],x);//从这个点继续找其儿子节点
  33. f1[x]:=f1[x]+min(min(f1[go[e]],f2[go[e]]),f3[go[e]]);//这个点之所以三种状态都能是因为,这个点要放了,那么其儿子节点也可以选择放或则不放,其较优解,//对于这个儿子节点没有被控制的情况,如果这个点放了,那么这个儿子节点就可以被控制了
  34. f3[x]:=f3[x]+f2[go[e]];//f3的意义是这个点没有被控制,但是子树都被控制了,这和2的含义正好相对应所以加上它就是累加f3的值
  35. if f1[go[e]]<=f2[go[e]] then bo:=true;//如果放更优那么bo就为true 否则就保持不变为false;(可以想象如果不放更优,那么就会出现这个父亲节点没人控制的情//况)
  36. f2[x]:=f2[x]+min(f1[go[e]],f2[go[e]]);//不放的话就不能加上第三种情况了,因为那样就会有一个点不受控制
  37. aa:=min(aa,f1[go[e]]-f2[go[e]]);//这是为了防止上述 “不放更优”的情况出现,那样我们必须在儿子中找一个“较优值"来控制这个父亲;
  38. end;
  39. e:=next[e];//找下一个儿子(father是fa)
  40. end;
  41. f1[x]:=f1[x]+1;
  42. if not bo then f2[x]:=f2[x]+aa;
  43. end;
  44.  
  45. procedure add(x,y:longint);
  46. begin
  47. inc(tot);
  48. next[tot]:=first[x];//采取邻接表的形式存储,我在群文件中将会给出邻接表的使用方法
  49. first[x]:=tot;
  50. go[tot]:=y;//存储其出度信息
  51. end;
  52.  
  53. begin
  54. assign(input,'virus.in');
  55. reset(input);
  56. assign(output,'virus.out');
  57. rewrite(output);
  58. readln(n);//读入n个点
  59. for i:=1 to n do//读入n个点的子节点
  60. begin
  61. read(x);//读入这个点
  62. read(k);//这个点的儿子个数
  63. for j:=1 to k do//读入儿子
  64. begin
  65. read(y);
  66. add(x+1,y+1);//因为是无向图所以加两次
  67. add(y+1,x+1);
  68. end;
  69. readln;
  70. end;
  71. cm(1,0);//1为儿子0为父亲
  72. writeln(min(f1[1],f2[1]));
  73. close(input);
  74. close(output);
  75. end.

//说一句题外话,很感谢ccy把他的题解拷给我...很感激真的,他的题解写的很详细很清楚,每一篇都让我学习到了很多很多...

认识你很荣幸,希望这样棒的人一切顺利。。阿里噶多

Day1:T3 bfs T4 树形DP的更多相关文章

  1. CCPC-Wannafly Winter Camp Day1 流流流动 (树形dp)

    题目描述 喜欢数学的wlswls最近被萎住了. 现在他一共有1...n1...n这么多数字,取数字ii会得到f[i]f[i]的收益.数字之间有些边,对于所有的i(i != 1)i(i!=1),若ii为 ...

  2. 【NOIP2016练习】T3 tree (树形DP)

    题意:一棵有N个结点的树,每个节点上有权值c[i] 需要选出若干结点,对于任意结点他的所有祖先都被选取且选取总个数不能超过lim 在此前提下使权值和最大 n,lim<=3000 思路:WA了1次 ...

  3. [IOI2008/BZOJ1791 岛屿](处理基环树的小技巧&基于bfs树形DP)

    IOI2008/BZOJ1791 岛屿 题目大意是在一个基环树森林里求每一棵基环树的直径①的和. 其实就是树的直径的基环树升级版.我们先把环找出来,然后从环上的每一个节点x出发,并且不经过环上其他节点 ...

  4. HDU 4123 (2011 Asia FZU contest)(树形DP + 维护最长子序列)(bfs + 尺取法)

    题意:告诉一张带权图,不存在环,存下每个点能够到的最大的距离,就是一个长度为n的序列,然后求出最大值-最小值不大于Q的最长子序列的长度. 做法1:两步,第一步是根据图计算出这个序列,大姐头用了树形DP ...

  5. 【NOIP2016】Day1 T3 换教室(期望DP)

    题目背景 NOIP2016 提高组 Day1 T3 题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 2n 节课程安排在 n 个时间段上. ...

  6. 车站分级 (2013noip普及组T4)(树形DP)

    题目描述 一条单向的铁路线上,依次有编号为 1,2,…,n 的 n个火车站.每个火车站都有一个级别,最低为 1 级.现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x ...

  7. 树形dp专题总结

    树形dp专题总结 大力dp的练习与晋升 原题均可以在网址上找到 技巧总结 1.换根大法 2.状态定义应只考虑考虑影响的关系 3.数据结构与dp的合理结合(T11) 4.抽直径解决求最长链的许多类问题( ...

  8. 【BZOJ-1864】三色二叉树 树形DP

    1864: [Zjoi2006]三色二叉树 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 659  Solved: 469[Submit][Status] ...

  9. BZOJ 2435 道路修建 NOI2011 树形DP

    一看到这道题觉得很水,打了递归树形DP后RE了一组,后来发现必须非递归(BFS) 递归版本84分: #include<cstdio> #include<cstring> #in ...

随机推荐

  1. javascript 实现一个网页,然后计算出有多少剩余时间的倒计时程序

    function counter() { var date = new Date(); var year = date.getFullYear(); var date2 = new Date(year ...

  2. 6天通吃树结构—— 第三天 Treap树

    原文:6天通吃树结构-- 第三天 Treap树 我们知道,二叉查找树相对来说比较容易形成最坏的链表情况,所以前辈们想尽了各种优化策略,包括AVL,红黑,以及今天 要讲的Treap树. Treap树算是 ...

  3. C#按LastID进行分页——与lambda形成链式

    public static class PageHelper { /// <summary> /// 按页码分页 /// </summary> /// <param na ...

  4. elasticsearch的rest搜索--- 安装

    目录: 一.针对这次装B 的解释 二.下载,安装插件elasticsearch-1.7.0   三.索引的mapping 四. 查询 五.对于相关度的大牛的文档 二.安装   1. 安装head管理插 ...

  5. WIN2003+IIS6+FastCGI+PHP5.4.30的安装配置

    原文:WIN2003+IIS6+FastCGI+PHP5.4.30的安装配置 说明:PHP5.5已不支持win2003了,Win2003最高能安装PHP5.4.30. 安装好系统:并且安装好IIS6. ...

  6. javascript中字符串常用方法总结

    字符串是javascript编程中不可或缺的元素,掌握字符串常用的方法也是我们学习过程中的必经之路,下面我们总结一些最常用的的字符串方法. string.charAt(postion) charAt方 ...

  7. passenger安装nginx

    1.更换淘宝gem gem sources --remove https://rubygems.org/ gem sources -a https://ruby.taobao.org/ 2.gem安装 ...

  8. 使用C语言编写windows服务一般框架

    原文:使用C语言编写windows服务一般框架 编写windows服务和编写windows应用程序一样,有一些回调函数必须填写且向windows 服务管理器(service manager)进行注册, ...

  9. 【转】Oracle修改表空间为自动扩展

    1.数据文件自动扩展的好处1)不会出现因为没有剩余空间可以利用到数据无法写入2)尽量减少人为的维护3)可以用于重要级别不是很大的数据库中,如测试数据库等 2.数据文件自动扩展的弊端1)如果任其扩大,在 ...

  10. .NET开源项目 TOP 25

    .NET开源项目 TOP 25 如果知道.NET项目在开源中国的git上所占的比重只有5%的话,为什么这个<2014年国人开发的最热门的开源软件TOP 100>榜中.NET项目那么少就是情 ...