• 道路重建(记忆化搜索+贪心)


    道路重建(记忆化搜索+贪心)

    一、题目

    道路重建

    时间限制: 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 }
  • 相关阅读:
    [转]给明年依然年轻的我们:欲望、外界、标签、天才、时间、人生目标、现实、后悔、和经历
    sql server 2005/2008 加密存储过程解密脚本/软件推荐
    将所有程序最小化到系统托盘RBTray strokeit TrayEverything
    [转]LeftLeaning RedBlack Tree
    emulator for the MIX computer (TAOCP)
    [转]Kindle电子书资源网站汇总
    鼻炎、鼻窦炎可尝试药膳
    C# 获取机器码(转)
    为输入提供动态提示框
    ProcessDialogKey 方法实现回车自动换行
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/7392575.html
Copyright © 2020-2023  润新知