题目:Explorer Bo
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5758
题意:给一棵树,树边的长度都是1,要求遍历他的所有边至少一次,起始点可以任意选,不能回头(但一条边可以遍历多次),可以传送到任意一点,在完成目的的基础上,传送次数越少越好(第一优先级),走过的路越短越好(第二优先级),输出走过的路的总长度。
思路:
这道题的难度不算很大,思路很容易就能想出来,但实现细节有点麻烦,比赛时候没去看有点可惜了(容我吹会B,实际上做这题花了一整个下午,WA到死啊,所以末尾附上一些我错过的测试数据)。
可以想象,传送次数是第一优先级,所以只有当遍历到叶子结点的时候才会使用传送,令 x = 叶子结点数,所以传送次数为:(x+1)/2,而每一次遍历都是从一个叶子结点到另一个叶子结点。
现在对每一条边进行初步计数,假设他下面有奇数个叶子结点,那么这条边要遍历一次,上图的A边初步计数就是遍历1次,因为他下面三个叶子结点可以自己搭配,剩下一个从A边通往外面和别人搭配。图2中A边初步预计要遍历2次,因为下面四个叶子结点不能全部自己搭配,如果这样,那红点就变成了一个新的叶子结点,可能会增加传送次数。所以只能有一对自己搭配,其他两个都要经过A边。
上面的初步计数可以通过深入优先搜索进行,如果树的叶子结点数是偶数个,那么答案已经出来了,现在要处理叶子结点奇数的情况。(处理原因就是奇数的情况下,叶子结点可以容许再出现一个,就像图二中红点一样)。
现在看图3,A边原本计数是2,但如果总的叶子结点数是奇数,那么我们可以让红点下面的四个点自己搭配,然后红点作为一个新的叶子结点再和别人配对,这样A边的计数就变成了1,原本B边的计数是1(奇数个叶子结点),现在就变成了2,因为如果红点和蓝点配对,那黑点又变成了一个新的叶子结点(但事实上只容许多出一个),所以B边得经过两次。
所以,我们就要从根结点开始深搜,每次都尝试对某个子树进行改变,将边1变成2,2变成1,然后每次都取最优(也就是子树的边计数减少最多的)。有了思路,代码实现就比较简单了,也可以参考我下面的AC代码。
AC代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<vector> 4 using namespace std; 5 struct ENode 6 { 7 int ad; 8 int num; 9 }; 10 vector<ENode> e[100010]; 11 bool u[100010]; 12 int sum=0; 13 int dfs(int rt,int fa,int fi) 14 { 15 u[rt]=1; 16 int s=0; 17 for(int i=0;i<e[rt].size();i++) 18 { 19 if(u[e[rt][i].ad]==0) 20 { 21 e[rt][i].num=dfs(e[rt][i].ad,rt,i); 22 s+=e[rt][i].num; 23 } 24 } 25 sum+=s; 26 if(s==0) s=1; 27 if(s&1) s=1; 28 else s=2; 29 return s; 30 } 31 int dfs2(int rt) 32 { 33 int mm=0; 34 for(int i=0;i<e[rt].size();i++) 35 { 36 if(e[rt][i].num==1) 37 { 38 int tmp=dfs2(e[rt][i].ad); 39 if(tmp-1>mm) mm=tmp-1; 40 } 41 else if(e[rt][i].num==2) 42 { 43 int tmp=dfs2(e[rt][i].ad); 44 if(tmp+1>mm) mm=tmp+1; 45 } 46 } 47 return mm; 48 } 49 int du[100010]; 50 int qiuCha(int n) 51 { 52 int co=0; 53 for(int i=1;i<=n;i++) 54 { 55 if(du[i]==1) co++; 56 } 57 if(co%2==0) return 0; 58 return dfs2(1); 59 } 60 int main() 61 { 62 int t,n,x,y; 63 scanf("%d",&t); 64 while(t--) 65 { 66 scanf("%d",&n); 67 for(int i=1;i<=n;i++) e[i].clear(); 68 memset(du,0,sizeof(du)); 69 for(int i=0;i<n-1;i++) 70 { 71 scanf("%d%d",&x,&y); 72 du[x]++; 73 du[y]++; 74 ENode tmp; 75 tmp.ad=y; 76 tmp.num=0; 77 e[x].push_back(tmp); 78 tmp.ad=x; 79 e[y].push_back(tmp); 80 } 81 memset(u,0,sizeof(u)); 82 ENode tmp; 83 tmp.ad=1; 84 sum=0; 85 dfs(1,0,0); 86 int cha=qiuCha(n); 87 printf("%d ",sum-cha); 88 } 89 return 0; 90 } 91 92 /* 93 易错数据: 94 6666 95 16 96 9 2 97 12 2 98 1 12 99 9 7 100 8 4 101 4 9 102 10 11 103 11 12 104 16 10 105 6 8 106 13 1 107 10 3 108 14 11 109 6 15 110 5 6 111 9 112 1 2 113 1 3 114 3 4 115 3 5 116 5 6 117 5 7 118 7 8 119 7 9 120 10 121 1 2 122 1 3 123 1 4 124 1 6 125 6 7 126 7 8 127 7 9 128 7 10 129 7 5 130 12 131 1 2 132 1 3 133 1 4 134 1 5 135 2 7 136 2 6 137 6 8 138 8 9 139 8 10 140 7 11 141 7 12 142 13 143 1 2 144 1 3 145 3 4 146 3 5 147 5 6 148 5 7 149 7 8 150 8 9 151 9 10 152 10 11 153 11 12 154 11 13 155 6 156 1 2 157 2 3 158 2 4 159 2 5 160 2 6 161 162 答案是: 163 18 164 9 165 9 166 12 167 13 168 5 169 */