• 最大独立集求解


    定义:

    独立集:在一个图中,找到一个集合包含的所有点相互之间都不存在连边

    最大独立集:在所有独立集中包含元素个数最多的独立集

    之前只是知道二分图的最大独立集 = 总点数-最大匹配数

    但是一般无向图的情况下求解就不能这样了

    换个角度思考,其实求最大独立集也是相当于建立一个相反图(把当前的边都去掉,添加上之前不被选中的边)

    就变成求修改之后的最大完全图的点的个数了,因为我们要保证选到的点之间不存在任何相连的边,那么图反过来之后,选到的点两两之间就必然

    存在边,否则说明之前的图是存在边的,也就是两个点不能全选中,而这样得到的就是完全图

    而我们求点数最多的那个,也就是最大完全图,也即最大团

    最近学习了一下这类问题的求解方式,这可以看做是一类搜索问题,不断dfs搜索找到最优解

    这样很容易看出 这是一个NP问题,复杂度也是 O(2^n)的

     所以优秀的剪枝是非常必要的

    定义dp[i] 是 i ~ N 这些点所能构成的最大团的点数

    那么我们就可以倒过来逐个算出dp[]值

    对于前面的dp[]值就要利用之前算出来的值来剪枝

    我们这里一个个从小到大添加节点,保证从当前出发添加进来的节点dp值已经求得

    例如当前添加了 v , 之前有了 t 个点了

    那么就可以用

    t+dp[v] <=mx

    t+N-t+1<=mx

    来剪枝了(这个仔细想一下就知道了)

    我们用一个_stack[][]数组记录能够扩展的节点,也就是这个数组中的点和之前取到的点每一个都存在连边,所以添加进来可以直接构成完全图

    我们只要每次再添加节点后更新这个数组就可以了

    因为是递归求解了,防止更新了会在回溯时出错,那么就讲数组定成两维的,第一维表示当前集合最大的点就可以直接用了

    int cnt = 0;
    for(int j=i+1 ; j<num ; j++){
      if(!mp[v][_stack[u][j]]) _stack[v][cnt++] = _stack[u][j];
    }

    然后POJ1419就是一道这个的裸题,只是每次记录了最优解要取到的点而已

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 #define N 105
     6 bool mp[N][N];
     7 int T , n , k;
     8 int dp[N] , ans[N] , tmp[N] , ret , mx , cnt;
     9 int _stack[N][N];
    10 
    11 void build(int k)
    12 {
    13     memset(mp , 0 , sizeof(mp));
    14     for(int i=1 ; i<=n ; i++) mp[i][i] = true;
    15     for(int i=0 ; i<k ; i++){
    16         int a , b;
    17         scanf("%d%d" , &a , &b);
    18         mp[a][b] = true;
    19     }
    20 }
    21 
    22 void dfs(int u , int num , int step)
    23 {
    24     if(num == 0){
    25         if(mx<step){
    26             mx = step;
    27             if(step>ret) for(int i=1 ; i<=step ; i++) ans[i] = tmp[i];
    28         }
    29         return;
    30     }
    31 
    32     for(int i=0 ; i<num ; i++){
    33         int v = _stack[u][i];
    34         //two methods of cut the node
    35         if(step+dp[v]<=mx) continue;
    36         if(step+n-v+1<=mx) continue;
    37         //从stack中能访问到v说明,v和之前所有点都有连边的,只要重新更新stack中的数据就可以了
    38         int cnt = 0;
    39         for(int j=i+1 ; j<num ; j++){
    40             if(!mp[v][_stack[u][j]]) _stack[v][cnt++] = _stack[u][j];
    41         }
    42         tmp[step+1] = v;
    43         dfs(v , cnt , step+1);
    44     }
    45 }
    46 
    47 int main()
    48 {
    49   //  freopen("a.in" , "r" , stdin);
    50     scanf("%d" , &T);
    51     while(T--){
    52         scanf("%d%d" , &n , &k);
    53         build(k);
    54         ret = 0; //init
    55         for(int i=n ; i>=1 ; i--){
    56             cnt = 0 , mx = 1;
    57             for(int j=i+1 ; j<=n ; j++){
    58                 if(!mp[i][j]) _stack[i][cnt++] = j;
    59             }
    60             tmp[1] = i;
    61             dfs(i , cnt , 1);
    62             dp[i] = mx;
    63             ret = max(ret , dp[i]);
    64         }
    65         printf("%d
    " , ret);
    66         for(int i=1 ; i<=ret ; i++){
    67             if(i<ret) printf("%d " , ans[i]);
    68             else printf("%d
    " , ans[i]);
    69         }
    70     }
    71     return 0;
    72 }
  • 相关阅读:
    C#中关于DBNULL的处理方法
    html 点击复制
    AJAX的简洁写法
    PHP 数组模糊查询
    PHP二维数组搜索返回数组
    php 数组排序得方法
    PHPExcel的使用
    使用PHPword中文乱码并且下载的方法
    关于多图上传的修改的操作
    把一个表里的两列或者三列合并为一行
  • 原文地址:https://www.cnblogs.com/CSU3901130321/p/4972300.html
Copyright © 2020-2023  润新知