道路重建(拓扑+贪心)
Posted 范仁义
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了道路重建(拓扑+贪心)相关的知识,希望对你有一定的参考价值。
道路重建(记忆化搜索+贪心)
一、题目
道路重建
时间限制: 1 Sec 内存限制: 128 MB
提交: 9 解决: 6
[提交][状态][讨论版]
题目描述
现在有一棵n个结点的树(结点从1到n编号),请问至少要删除几条边,才能得到一个恰好有p个结点的子树?
输入
第一行输入两个数n和p (1 <= n<= 150, 1 <= p<= n)
接下来输入n-1行,每行两个整数x y,表示x和y之间有一条边。
输出
输出答案。
样例输入
11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11
样例输出
2
提示
如果1-4 和 1-5 两条边删除,结点1, 2, 3, 6,
7, 8会形成一颗有6个结点的子树。
来源
二、分析
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 5 struct node{ 6 int nameNum;//nameNum表示节点的编号 7 int val;//val表示节点i的所有孩子(包括直接和间接)与它本身之和 8 }; 9 struct node a[155];//用a[i].val表示节点i的所有孩子(包括直接和间接)与它本身之和 10 bool vis[155];//vis[i]==false表示节点i没有被访问过 11 bool adjacencyMatrix[155][155];//adjacencyMatrix[i][j]=true表示i是j的父亲 12 int n,p; 13 int fatherSum[155];//fatherSum[i]表示congi节点出发到它的祖先,经过的节点数,也就是它和他所有祖先的和 14 int father[155][155];//father[i][j]记录i号节点的第j个父亲 //记忆化搜索,把所有找过的节点的父亲都记录下来 15 16 int minStep; 17 int choose[155];//记录所有被选的边 18 int ans[155]; 19 20 bool operator <(node p1,node p2){ 21 return p1.val>p2.val; 22 } 23 24 void printArr(); 25 void printArr_a(); 26 void printArr_ans(); 27 28 void readData(){ 29 cin>>n>>p; 30 int father1,child1; 31 for(int i=1;i<n;i++){ 32 cin>>father1>>child1; 33 a[father1].nameNum=father1; 34 a[child1].nameNum=child1; 35 //记录直接孩子的数目 36 //a[father1].val++; 37 adjacencyMatrix[father1][child1]=true; 38 } 39 } 40 41 42 //记忆化搜索,把所有找过的节点的父亲都记录下来 43 //回溯找i的所有父亲,给它的所有父亲的a[i]加1 44 void findFather(int child){ 45 if(child==1) return; 46 for(int i=1;i<=n;i++){ 47 if(adjacencyMatrix[i][child]){ 48 a[i].val++; 49 father[child][0]=father[i][0]+1; 50 father[child][father[child][0]]=i; 51 if(father[i][0]!=0){//说明i节点的父亲我已经找过了 52 for(int j=1;j<=father[i][0];j++){ 53 father[child][j]=father[i][j]; 54 a[father[i][j]].val++; 55 } 56 } 57 else findFather(i); 58 break; 59 } 60 } 61 } 62 63 //填充数组a 64 void findArr_a(){ 65 for(int i=1;i<=n;i++){//对1-n这n个节点找父亲 66 a[i].val++; 67 findFather(i); 68 } 69 } 70 71 void init(){ 72 readData(); 73 findArr_a();//回溯法找a[i] 74 //printArr_a(); 75 //printArr(); 76 } 77 78 //下一次搜索的时候,我肯定没有必要再从n开始搜索 79 //如果前minstep根都达不到长度,那么所有后面的都不用看了,所以minStep的初始值就是n 80 //如果这一根和上一根相同,如果上一根不可以,那么这一根也不用尝试了 81 void search(int m,int step,int start){ 82 if(m==0){ 83 if(step<minStep){ 84 minStep=step; 85 for(int i=1;i<=minStep;i++){ 86 ans[i]=choose[i]; 87 } 88 } 89 90 }else 91 for(int i=start;i<=n;i++){ 92 //如果step大于等于了minStep,它及它后面的都不用考虑了 93 if(step>=minStep) return; 94 95 //如果在choose里面有他的父亲,那他就不用考虑了 96 int findFather=false; 97 // for(int j=1;j<=step;j++){ 98 // for(int k=1;k<=father[a[i].nameNum][0];k++){ 99 // if(choose[j]==father[a[i].nameNum][k]){ 100 // findFather=true; 101 // } 102 // } 103 // } 104 for(int j=1,k=1;j<=step&&k<=father[a[i].nameNum][0];){ 105 if(choose[j]==father[a[i].nameNum][k]){ 106 findFather=true; 107 break; 108 }else if(choose[j]<father[a[i].nameNum][k]){ 109 j++; 110 }else if(choose[j]>father[a[i].nameNum][k]){ 111 k++; 112 } 113 } 114 if(findFather) continue; 115 116 //如果这一根和上一根相同,如果上一根不可以,那么这一根也不用尝试了 117 if(i>1&&a[i].val==a[i-1].val&&!vis[i-1]) continue; 118 119 //如果前minstep根都达不到长度,那么所有后面的都不用看了,所以minStep的初始值就是n 120 if(minStep!=n){ 121 int sum=0; 122 int k=minStep; 123 int i1=i; 124 while(k--){ 125 sum+=a[i1++].val; 126 } 127 if(sum<m) return; 128 } 129 130 if(!vis[i]&&a[i].val<=m){ 131 vis[i]=true; 132 choose[step+1]=a[i].nameNum; 133 search(m-a[i].val,step+1,i+1); 134 } 135 vis[i]=false; 136 } 137 } 138 139 void findans(){ 140 int m=n-p; 141 minStep=n; 142 search(m,0,1); 143 cout<<minStep<<endl; 144 printArr_ans(); 145 } 146 147 int main() { 148 freopen("src/test8.txt","r",stdin); 149 //freopen("src/test2.txt","r",stdin); 150 //freopen("src/RoadRebuild.txt","r",stdin); 151 init(); 152 sort(a+1,a+n+1); 153 //printArr_a(); 154 findans(); 155 return 0; 156 } 157 158 void printArr_ans(){ 159 for(int i=1;i<=minStep;i++){ 160 cout<<ans[i]<<" "; 161 } 162 cout<<endl; 163 } 164 165 void printArr_a(){ 166 for(int i=1;i<=n;i++){ 167 cout<<a[i].val<<" "<<a[i].nameNum<<" "; 168 } 169 cout<<endl; 170 } 171 172 void printArr(){ 173 cout<<"Arr a"<<endl; 174 cout<<"--------------------------------------------------------------------"<<endl; 175 for(int i=1;i<=n;i++){ 176 cout<<a[i].val<<" "; 177 } 178 cout<<endl; 179 180 cout<<"Arr fatherSum"<<endl; 181 cout<<"--------------------------------------------------------------------"<<endl; 182 for(int i=1;i<=n;i++){ 183 cout<<fatherSum[i]<<" "; 184 } 185 cout<<endl; 186 187 188 cout<<"Arr vis"<<endl; 189 cout<<"--------------------------------------------------------------------"<<endl; 190 for(int i=1;i<=n;i++){ 191 cout<<vis[i]<<" "; 192 } 193 cout<<endl; 194 195 cout<<"Arr adjacencyMatrix"<<endl; 196 cout<<"--------------------------------------------------------------------"<<endl; 197 for(int i=1;i<=n;i++){ 198 for(int j=1;j<=n;j++){ 199 cout<<adjacencyMatrix[i][j]<<" "; 200 } 201 cout<<endl; 202 } 203 cout<<endl; 204 205 cout<<"Arr father"<<endl; 206 cout<<"--------------------------------------------------------------------"<<endl; 207 for(int i=1;i<=n;i++){ 208 for(int j=1;j<=father[i][0];j++){ 209 cout<<father[i][j]<<" "; 210 } 211 cout<<endl; 212 } 213 cout<<endl; 214 215 }
以上是关于道路重建(拓扑+贪心)的主要内容,如果未能解决你的问题,请参考以下文章