Day1:T3 bfs T4 树形DP

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大神详细的注释

 

直接附上标程:

//感谢笑尘的思路

const

  dx:array[1..4] of longint=(0,0,1,-1);

  dy:array[1..4] of longint=(1,-1,0,0);//四个方向

var

  h:array[1..1000000] of record

//队列 x,y表示坐标  g中1表示上升状态,2表示下降状态  l表示用掉几个装置

                      x,y:longint;

                           g:1..2;

                           l:longint;

                           end;

  dis:array[-1..105,-1..105,1..2] of longint;//每个坐标的最优解(用掉几个装置)

  a:array[-1..105,-1..105] of longint;//存地图

  n,m,i,j,head,tail,x1,y1,x2,y2,p,cishu,k:longint;



function min(a,b:longint):longint;//返回较小值

begin

  if a>b then exit(b) else exit(a);

end;



begin

  fillchar(a,sizeof(a),0);

  fillchar(dis,sizeof(dis),$7f div 3);//初始化值要大

  readln(m,n);

  for i:=1 to m do

    begin

      for j:=1 to n do

        read(a[i,j]);

      readln;

    end;

  head:=0;tail:=2;

  h[1].x:=0;h[1].y:=0;h[1].g:=1;h[1].l:=0;//这是第一个是上升状态的情况

  h[2].x:=0;h[2].y:=0;h[2].g:=2;h[2].l:=0;//这是第一个是下降状态的情况

  dis[0,0,1]:=0;//最优解一开始(0,0)坐标都是0

  dis[0,0,2]:=0;

  while head<tail do

    begin

      inc(head);

      x1:=h[head].x;y1:=h[head].y;p:=h[head].g;cishu:=h[head].l;//x1,y1是坐标(从队列中取出),p是当前状态,cishu是转//换次数

      for i:=1 to 4 do

        begin

          x2:=x1+dx[i];

          y2:=y1+dy[i];

          if (x2>=0) and (x2<=m+1) and (y2>=0) and (y2<=n+1) then//如果这四个方向符合

            begin

              if (a[x2,y2]>=a[x1,y1]) and (p=1) and (dis[x2,y2,1]>cishu) then

//如果目标点的高度大于等于当前的点的高度,而且当前处于上升阶段 那么cishu不需要加1,直接和目标点的最优解比较。如果可//以更新则更新

                begin

                  dis[x2,y2,1]:=cishu;//更新dis节点

                  inc(tail);

                  h[tail].x:=x2;

                  h[tail].y:=y2;

                  h[tail].g:=1;

                  h[tail].l:=cishu;

                end;

              if (a[x2,y2]>a[x1,y1]) and (p=2) and (dis[x2,y2,1]>cishu+1) then

//如果目标点的高度大于当前的点的高度且处于下降状态 那么cishu+1,用cishu+1和目标点的最优解比较

                begin

                  dis[x2,y2,1]:=cishu+1;//更新dis节点

                  inc(tail);

                  h[tail].x:=x2;

                  h[tail].y:=y2;

                  h[tail].g:=1;

                  h[tail].l:=cishu+1;

                end;

              if (a[x2,y2]<a[x1,y1]) and (p=1) and (dis[x2,y2,2]>cishu+1) then

//如果目标点的高度小于当前的点,且当前处于上升状态 那么cishu+1,用cishu+1和目标点的最优解比较

                begin

                  dis[x2,y2,2]:=cishu+1;//更新dis节点

                  inc(tail);

                  h[tail].x:=x2;

                  h[tail].y:=y2;

                  h[tail].g:=2;

                  h[tail].l:=cishu+1;

                end;

              if (a[x2,y2]<=a[x1,y1]) and (p=2) and (dis[x2,y2,2]>cishu) then

//如果目标点的高度小于或等于当前的点,且当前处于下降状态 那么cishu不需要+1,用cishu和目标点的最优解比较

                begin

                  dis[x2,y2,2]:=cishu;//更新dis节点

                  inc(tail);

                  h[tail].x:=x2;

                  h[tail].y:=y2;

                  h[tail].g:=2;

                  h[tail].l:=cishu;

                end;

            end;

        end;

    end;//直到队列结束

  for i:=1 to m do

    begin

      for j:=1 to n do

        begin

          k:=min(dis[i,j,1],dis[i,j,2]);//先找出dis中的上升状态的最优解和下降状态的最优解 取最小值

          if (k=0) and (a[i,j]=0) and ((i=1) or (i=m) or (j=1) or (j=n)) then

            if j=1 then write(0) else write(' 0')//如果这个点在边缘地区(边界) 且这个点的高度为0 那么就不需要转化装//置

              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]中求更优解,这两个加起来表示其不用相同的方式回去。

                 write(' ',min(2*k+1,dis[i,j,1]+dis[i,j,2]));//之所以判断j=1是为了防止末尾输出多余空格

        end;

      writeln;

    end;

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的原因,可以看学长的注释:

如果父亲放了人,那么孩子就不需要放人了,当然也可以放人,所以可以直接取它们的最小值。如果父亲不放人,孩子至少要有一个放人,否则就看守不住父亲。所以,此时要找一个最优的孩子u来放人。怎么找到u呢?



我们可以这样考虑:穷举所有的孩子节点,用D[i]表示孩子节点i的DP[I,1]-DP[I,0]的值。这个值越小,说明这个节点放人越好。于是找到所有孩子中这个值的最小值,把这时的i作为u节点使用。

 

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

 自己乱yy了一下

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

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

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

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

#include<cstdio>

#include<cstring>

#include<iostream>

using namespace std;

int f1[1500],f2[1500],f3[1500];

int tot=0,u,n,x,k,y;

struct node{

	int from,to,next;

}a[1500];

void add(int x,int y){

	a[++tot].next=a[x].from;

	a[x].from=tot;

	a[tot].to=y;

}

void check(int x,int fa){

	u=1500;

	bool f=false;

	int e=a[x].from;

	while(e!=0){

		if(a[e].to!=fa){

			check(a[e].to,x);

			f1[x]=f1[x]+min(min(f1[a[e].to],f2[a[e].to]),f3[a[e].to]);

			f3[x]=f3[x]+f2[a[e].to];

			if(f1[a[e].to]>f2[a[e].to]) f=true;

			f2[x]=f2[x]+min(f1[a[e].to],f2[a[e].to]);

			u=min(u,f1[a[e].to]-f2[a[e].to]);

		}

		e=a[e].next;

	}

	f1[x]++;

	if(f) f2[x]=f2[x]+u;

}

int main(){

	scanf("%d",&n);

	for(int i=1;i<=n;i++){

		scanf("%d%d",&x,&k);

		for(int j=1;j<=k;j++){

			scanf("%d",&y);

			add(x+1,y+1);

			add(y+1,x+1);

		}

	}

	check(1,0);

	printf("%d",min(f1[1],f2[1]));

	return 0;

}

 

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

这个程序:(可以看看我画的示意图)

采用邻接表的形式存储边信息(因为是无向图所以要多存一条);



f1表示父亲这个点放人看守 并且这个点已经被控制到了,即有人监视着这台电脑.

f2表示父亲这个点不放人看守,但是这个点已经被控制到了.

f3表示父亲这个点没有人控制到,但是其子树全部都被控制到了。



var

  i,j,n,x,k,y,tot:longint;

  f1,f2,f3:array[0..2000] of longint;

  first,next,go:array[0..10010] of longint;



function min(a,b:longint):longint;

begin

  if a>b then exit(b) else

     exit(a);

end;



procedure cm(x,fa:longint);//x的父亲是fa

var

  bo:boolean;

  e,aa:longint;



begin

  aa:=10000;

  bo:=false;

  e:=first[x];//找到表头

  while e<>0 do//如果还有出度

    begin

      if go[e]<>fa then//如果这个点的出度不是父亲节点

        begin

          cm(go[e],x);//从这个点继续找其儿子节点

          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]]);//这是为了防止上述 “不放更优”的情况出现,那样我们必须在儿子中找一个“较优值"来控制这个父亲;

        end;

      e:=next[e];//找下一个儿子(father是fa)

    end;

  f1[x]:=f1[x]+1;

  if not bo then f2[x]:=f2[x]+aa;

end;



procedure add(x,y:longint);

begin

  inc(tot);

  next[tot]:=first[x];//采取邻接表的形式存储,我在群文件中将会给出邻接表的使用方法

  first[x]:=tot;

  go[tot]:=y;//存储其出度信息

end;



begin

  assign(input,'virus.in');

  reset(input);

  assign(output,'virus.out');

  rewrite(output);

  readln(n);//读入n个点

  for i:=1 to n do//读入n个点的子节点

    begin

      read(x);//读入这个点

      read(k);//这个点的儿子个数

      for j:=1 to k do//读入儿子

        begin

          read(y);

          add(x+1,y+1);//因为是无向图所以加两次

          add(y+1,x+1);

        end;

      readln;

    end;

  cm(1,0);//1为儿子0为父亲

  writeln(min(f1[1],f2[1]));

  close(input);

  close(output);

end.

 

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

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

你可能感兴趣的:(bfs)