1. 程式人生 > >【新手向】最大團問題和最大獨立子集的懶人演算法(隨機化)

【新手向】最大團問題和最大獨立子集的懶人演算法(隨機化)

首先,團是什麼呢?
團就是一個點集,點集中任意兩點都有直接的邊相連
舉個栗子:圖中紅色的點構成了一個團
圖中紅色的點構成了一個團,當然單獨一個點也算是一個團。

那麼,獨立子集又是什麼呢?
和團正好相反,獨立子集也是一個點集,但是任意兩點之間都沒有直接的邊相連……
再舉個栗子……
圖中綠色的點構成了一個獨立子集
圖中綠色的點構成了一個獨立子集,你說一個點是一個獨立子集我也沒意見……

那麼,最大團和最大獨立子集怎麼求呢?
首先最大獨立子集和最大團正好相反,所以最大獨立子集的數目就是這個圖的“補圖”的最大團……
(補圖就是一張圖有邊的改成沒邊,沒邊的改成有邊……)
於是最大獨立子集問題轉化成最大團問題

最大團又怎麼求呢???
最大團其實是個NP完全問題……目前常用的演算法是搜尋+優化和隨機化……
這裡介紹隨機化演算法(適合懶人……)
把點組成隨機組成一個排列,然後從前往後暴力找……首先第一個點可以組成一個最大團,然後加入第二個,看看是不是,如果是,加入第三個,如果不是,不加入第二個,直接加入第三個……這樣跑一邊是n^2的,但是答案並不一定正確,所以需要多次隨機,然後取max……
這樣的準確性還是挺高的,程式碼也不難打,這裡還是貼出來吧……(程式碼風格鬼畜,請做好心理準備!!!)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
using namespace std;
bool a[120][120];
int b[120];
bool p[120];
int n,m,x,y;

void RANDOM()
{
    srand(time(0));
    for(int i = 1;i <= n;i ++)
        swap(b[i],b[rand()%n+1]);
    return ;
}

int JUDGE()
{
    for
(int i = 1;i <= n;i ++) p[i] = 0; int ans = 0; for(int i = 1;i <= n;i ++) { if(!p[i]) { ans ++; for(int j = i+1;j <= n;j ++) { if(!a[b[i]][b[j]]) p[j] = 1; } } } return
ans; } int main() { cin >> n >> m; for(int i = 1;i <= m;i ++) { cin >> x >> y; a[x][y] = 1; a[y][x] = 1; } for(int i = 1;i <= n;i ++) b[i] = i; int maxn = 0; for(int i = 1;i <= 1000;i ++) { RANDOM(); maxn = max(maxn,JUDGE()); } cout << maxn << endl; return 0; }