[Poj]3659——树形DP
[题目大意]
- 给定一棵树,在某一个点上放置一个基站需要花费1单位的代价,可以覆盖自己和相邻的点,求覆盖所有点的最小代价。
[分析题解]
- 对于一个点来说,其能被覆盖只有三种方法:父亲放置基站,自己放置基站,某个儿子放置基站。这样的话可以想到枚举状态来动态规划。
- 动态规划的状态表示方法是非常多的,我的表示比较繁琐,是这样的,先建立有根树,然后用F[I,J]表示以I为根的子树,状态为J的最小覆盖代价。
- J=0代表I的父亲没有放,I不放置。这样,就要从I的儿子中找一个放基站,才能保证I被覆盖。其他的儿子随意。
- J=1代表I的父亲放置了,I不放置。这样I的儿子可以随意,爱放不放,反正I已经被覆盖,儿子的事情那是子问题里需要解决的。
- J=2代表I的父亲没有放,I放置了。既然I已经放置了,儿子也是随意放。但是代价要+1,因为在I处放置了一个。
- J=3代表I的父亲放置了,I放置了。同上。其实2、3可以合并为一种情况,为了显得厉害一些(其实是脑子小),就拆成两个了。
- 这样按照定义转移一下就好了,最后输出Min(F[Root,0],F[Root,2]);
[个人代码]
View Code
1 //10082338 perseawe 3659 Accepted 1356K 79MS Pascal 2528B 2012-04-16 22:41:13
2
3 Const
4 MaxNode=10000+100;
5 MaxEdge=MaxNode*2;
6 Inf=1000000;
7
8 Var
9 n,tot:Longint;
10 Flag:Array [0..MaxNode] of Boolean;
11 hv,Line,lv,Father:Array [0..MaxNode] of Longint;
12 F:Array [0..MaxNode,0..3] of Longint;
13 Edge:Array [0..MaxEdge] of Record next,wh:Longint;end;
14
15 Function Min(a,b:Longint):Longint;begin if a<b then exit(a);exit(b);end;
16
17 Procedure AddEdge(u,v:Longint);
18 begin
19 inc(tot);
20 Edge[tot].next:=hv[u];hv[u]:=tot;
21 Edge[tot].wh:=v;
22 inc(tot);
23 Edge[tot].next:=hv[v];hv[v]:=tot;
24 Edge[tot].wh:=u;
25 end;
26
27 Procedure Init;
28 var
29 i,u,v:Longint;
30 begin
31 readln(n);
32 for i:=1 to n-1 do
33 begin
34 readln(u,v);
35 AddEdge(u,v);
36 end;
37 end;
38
39 Procedure BFS_Lebal;
40 var
41 I,tnode,u,v,top,tail:Longint;
42 begin
43 For I:=N downto 1 do
44 begin
45 F[I,0]:=Inf;
46 F[I,1]:=0;
47 F[I,2]:=1;
48 F[I,3]:=1;
49 end;
50 Fillchar(lv,sizeof(lv),0);Lv[1]:=1;
51 top:=0;tail:=1;Line[1]:=1;
52 Repeat
53 inc(top);
54 u:=Line[top];
55 tnode:=hv[u];
56 while tnode<>0 do
57 begin
58 v:=Edge[tnode].wh;
59 if lv[v]=0 then
60 begin
61 F[u,0]:=0;
62 lv[v]:=lv[u]+1;
63 Father[v]:=u;
64 inc(tail);
65 Line[tail]:=v;
66 end;
67 tnode:=Edge[tnode].next;
68 end;
69 Until top=tail;
70 end;
71
72 Procedure DP;
73 var
74 I,u,v,Tmp,Tnode,Plus:Longint;
75 begin
76 Fillchar(Flag,Sizeof(Flag),False);
77 For I:=N downto 1 do
78 begin
79 u:=Line[I];
80 //UpDate F[u,0]
81 if not(Flag[u]) then
82 begin
83 tnode:=hv[u];
84 Tmp:=MaxLongint;Plus:=0;
85 While tnode<>0 do
86 begin
87 v:=Edge[tnode].wh;
88 if lv[u]+1=lv[v] then if F[v,2]<Tmp then begin Tmp:=F[v,2];Plus:=F[v,2]-F[v,0];end;
89 tnode:=Edge[tnode].next;
90 end;
91 Inc(F[u,0],Plus);
92 end;
93 //UpDate His Father
94 Tmp:=Min(F[u,0],F[u,2]);
95 Inc(F[Father[u],0],Tmp);
96 Inc(F[Father[u],1],Tmp);
97 If Tmp=F[u,2] then Flag[Father[u]]:=True;
98
99 Tmp:=Min(F[u,1],F[u,3]);
100 Inc(F[Father[u],2],Tmp);
101 Inc(F[Father[u],3],Tmp);
102 end;
103 end;
104
105 Procedure Main;
106 begin
107 BFS_Lebal;
108 DP;
109 end;
110
111 Procedure Print;
112 begin
113 writeln(Min(F[1,0],F[1,2]));
114 end;
115
116 Begin
117 assign(INPUT,'tower.in');Reset(INPUT);
118 Init;
119 Close(INPUT);
120 Main;
121 assign(OUTPUT,'tower.out');Rewrite(OUTPUT);
122 Print;
123 Close(OUTPUT);
124 End.
2
3 Const
4 MaxNode=10000+100;
5 MaxEdge=MaxNode*2;
6 Inf=1000000;
7
8 Var
9 n,tot:Longint;
10 Flag:Array [0..MaxNode] of Boolean;
11 hv,Line,lv,Father:Array [0..MaxNode] of Longint;
12 F:Array [0..MaxNode,0..3] of Longint;
13 Edge:Array [0..MaxEdge] of Record next,wh:Longint;end;
14
15 Function Min(a,b:Longint):Longint;begin if a<b then exit(a);exit(b);end;
16
17 Procedure AddEdge(u,v:Longint);
18 begin
19 inc(tot);
20 Edge[tot].next:=hv[u];hv[u]:=tot;
21 Edge[tot].wh:=v;
22 inc(tot);
23 Edge[tot].next:=hv[v];hv[v]:=tot;
24 Edge[tot].wh:=u;
25 end;
26
27 Procedure Init;
28 var
29 i,u,v:Longint;
30 begin
31 readln(n);
32 for i:=1 to n-1 do
33 begin
34 readln(u,v);
35 AddEdge(u,v);
36 end;
37 end;
38
39 Procedure BFS_Lebal;
40 var
41 I,tnode,u,v,top,tail:Longint;
42 begin
43 For I:=N downto 1 do
44 begin
45 F[I,0]:=Inf;
46 F[I,1]:=0;
47 F[I,2]:=1;
48 F[I,3]:=1;
49 end;
50 Fillchar(lv,sizeof(lv),0);Lv[1]:=1;
51 top:=0;tail:=1;Line[1]:=1;
52 Repeat
53 inc(top);
54 u:=Line[top];
55 tnode:=hv[u];
56 while tnode<>0 do
57 begin
58 v:=Edge[tnode].wh;
59 if lv[v]=0 then
60 begin
61 F[u,0]:=0;
62 lv[v]:=lv[u]+1;
63 Father[v]:=u;
64 inc(tail);
65 Line[tail]:=v;
66 end;
67 tnode:=Edge[tnode].next;
68 end;
69 Until top=tail;
70 end;
71
72 Procedure DP;
73 var
74 I,u,v,Tmp,Tnode,Plus:Longint;
75 begin
76 Fillchar(Flag,Sizeof(Flag),False);
77 For I:=N downto 1 do
78 begin
79 u:=Line[I];
80 //UpDate F[u,0]
81 if not(Flag[u]) then
82 begin
83 tnode:=hv[u];
84 Tmp:=MaxLongint;Plus:=0;
85 While tnode<>0 do
86 begin
87 v:=Edge[tnode].wh;
88 if lv[u]+1=lv[v] then if F[v,2]<Tmp then begin Tmp:=F[v,2];Plus:=F[v,2]-F[v,0];end;
89 tnode:=Edge[tnode].next;
90 end;
91 Inc(F[u,0],Plus);
92 end;
93 //UpDate His Father
94 Tmp:=Min(F[u,0],F[u,2]);
95 Inc(F[Father[u],0],Tmp);
96 Inc(F[Father[u],1],Tmp);
97 If Tmp=F[u,2] then Flag[Father[u]]:=True;
98
99 Tmp:=Min(F[u,1],F[u,3]);
100 Inc(F[Father[u],2],Tmp);
101 Inc(F[Father[u],3],Tmp);
102 end;
103 end;
104
105 Procedure Main;
106 begin
107 BFS_Lebal;
108 DP;
109 end;
110
111 Procedure Print;
112 begin
113 writeln(Min(F[1,0],F[1,2]));
114 end;
115
116 Begin
117 assign(INPUT,'tower.in');Reset(INPUT);
118 Init;
119 Close(INPUT);
120 Main;
121 assign(OUTPUT,'tower.out');Rewrite(OUTPUT);
122 Print;
123 Close(OUTPUT);
124 End.
[相关链接]
- 如果继续深入研究一下,这个东西其实是树的最小支配集。可以用贪心来解决。“随便找一个根,后续遍历树。每个点,如果它的孩子都没有真正覆盖的,同时他自己和他父亲都没有真正覆盖的,就把它的父亲覆盖,答案加一。”贪心算法及代码转自这里1 #include<iostream>
2 #include<vector>
3 using namespace std;
4 int n, ans;
5 vector<int> v[10001];
6 bool vst[10001], have[10001];
7 void jeo(int id, int from)
8 {
9 vst[id] = 1; bool flag = false;
10 for(int i = 0; i < v[id].size(); i++)
11 if (!vst[v[id][i]])
12 jeo(v[id][i], id), flag = flag || have[v[id][i]];
13 if (from == -1) ans += !(have[id] || flag);
14 else if (!have[from] && !have[id] && !flag)
15 have[from] = 1, ans++;
16 }
17 int main()
18 {
19 scanf("%d", &n);
20 for(int i = 1; i < n; i++)
21 {
22 int a, b;scanf("%d%d", &a, &b);
23 v[a].push_back(b);
24 v[b].push_back(a);
25 }
26 memset(vst, 0, sizeof(bool) * (n + 1));
27 memset(have, 0, sizeof(bool) * (n + 1));
28 ans = 0; jeo(1, -1);
29 printf("%d\n", ans);
30 return 0;
31 }
[启发总结]
- 动态规划的本质还是枚举,当然是很聪明的枚举。
- 动态规划的子问题性可以看成是一个个单独的程序,对于当前的这个状态,我们要设计出一个合法的通道来与这些程序进队链接。就像本题,通道就是状态之间的转移,我们只关心当前的问题怎么解决,只与子问题怎么解决,那是子问题与边界的事,当前状态不关心。
状态的设计只要能保证合法性、信息的完整性、信息的可还原性即可。如我用F[I,J]中的I来定位,J作为状态编号,用这两个数字就可以确定出现在I结点出的状态。
——————————————————————————————————————
你说,我们的存在,永不消逝。对吧?
如果,我们都在努力创造了存在。我们,会幸福的。对吧?