道路重建(拓扑+贪心)

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 }

 

以上是关于道路重建(拓扑+贪心)的主要内容,如果未能解决你的问题,请参考以下文章

道路重建 (2018山东冬令营)

贪心算法:根据身高重建队列(续集)

P1272 重建道路

洛谷——P3905 道路重建

洛谷 P3905 道路重建

luogu P1272 重建道路