1. 程式人生 > >【最大團】【HDU1530】【Maximum Clique】

【最大團】【HDU1530】【Maximum Clique】

先上最大團定義:

最大團問題(Maximum Clique Problem, MCP)是圖論中一個經典的組合優化問題,也是一類NP完全問題,在國際上已有廣泛的研究,而國內對MCP問題的研究則還處於起步階段,因此,研究最大團問題具有較高的理論價值和現實意義。 最大團問題又稱為最大獨立集問題(Maximum Independent Set Problem)。啟發式演算法。確定性演算法有回溯法分支限界法等,啟發式演算法、蟻群演算法、順序貪婪演算法、DLS-MC演算法和智慧搜尋演算法等。

給定無向圖G=(V,E)。如果UV,且對任意u,vU 有(u,v)  E,則稱U 是G 的完全子圖。

團就是完全子圖 最大團就是點數最多的完全子圖 通俗點講就是在一個無向圖中找出一個點數最多的完全圖
      英文描述如下: Given a graph G(V, E), a clique is a sub-graph g(v, e), so that for all vertex pairs v1, v2 in v, there exists an edge (v1, v2) in e. Maximum clique is the clique that has maximum number of vertex.
獨立思考演算法: n<=50
深搜? dfs(p); visit[p]=1;
1.
    for(int i=1;i<=n;i++) 
    if(visit[i]==1)
    dfs(i);
 2.然後假設 i這個點就是在大團裡面的啦:
 如果存在 map[i][k]=0;
 visit[k]=0; 代表不能選取(回溯時記得標誌清理乾淨)
  for(int i=1;i<=n;i++) 
   if(visit[i]==1)
   dfs(i);
最後數visit的1的數目更新答案.....

顯然這是暴力演算法...不過先寫一發,看看有多暴力


10s都T了...好怕怕。。 怎麼優化? 最優化剪枝:      ans-現在團中節點>可加入節點(加入該剪枝已經可以過) 好吧 以遞增順序列舉點才是搜尋剪枝王道  1 2 3 4 5 這個狀態就有  5!個不同的順序描述方式
程式碼
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>

 using namespace std;

const int MAXN=60;
int N;
int Path[MAXN][MAXN];
int Set[MAXN];//用以尋找一個可能集合
int Ans;

//判斷新點是否與原集合裡的點互相連通
bool is_clique( const int end, const int point )
{
    for( int i=1; i<end; ++i )
    {
        if( !Path[ Set[i] ][point] )
        {
            return false;
        }
    }
    return true;
}
//遞迴查詢,查詢演算法簡單明瞭,不再詳說
void dfs( int depth, int now )
{
    if( depth+N-now+1 <= Ans )
    {
        return;
    }
    for( int i=now; i<=N; ++i )
    {
        if( is_clique(depth+1, i) )
        {
            Set[depth+1]=i;
            dfs( depth+1, i+1 );
        }
    }
    if( depth > Ans )
    {
        Ans=depth;
    }
}
int main()
{
    freopen("in","r",stdin);

    while( scanf("%d", &N)!=EOF && N )
    {
        for( int i=1; i<=N; ++i )
        {
            for( int j=1; j<=N; ++j )
            {
                scanf("%d", &Path[i][j]);
            }
        }
        Ans=0;
        dfs( 0, 1 );
        printf("%d\n", Ans);
    }
    return 0;
}
//還可以設定一個DP[MAXN],用以記錄i~N點集合裡最大團數,可以利用這一點剪枝. DP[n]=DP[n+1]+1 or DP[n+1];


有點慢 嘗試加入DP剪枝。
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>

 using namespace std;

const int MAXN=60;
int N;
int Path[MAXN][MAXN];
int Set[MAXN];//用以尋找一個可能集合
int Ans;
int DP[MAXN];
//判斷新點是否與原集合裡的點互相連通
bool is_clique( const int end, const int point )
{
    for( int i=1; i<end; ++i )
    {
        if( !Path[ Set[i] ][point] )
        {
            return false;
        }
    }
    return true;
}
//遞迴查詢,查詢演算法簡單明瞭,不再詳說
void dfs( int depth, int now )
{
    if( depth+N-now+1 <= Ans||depth+DP[now] <=Ans )
    {
        return;
    }
    for( int i=now; i<=N; ++i )
    {
        if( is_clique(depth+1, i) )
        {
            Set[depth+1]=i;
            dfs( depth+1, i+1 );
        }
    }
    if( depth > Ans )
    {
        Ans=depth;
    }
}
int main()
{
//    freopen("a.in","r",stdin);

    while( scanf("%d", &N)!=EOF && N )
    {
        for( int i=1; i<=N; ++i )
        {
            for( int j=1; j<=N; ++j )
            {
                scanf("%d", &Path[i][j]);
            }
        }
        memset(DP,0,sizeof(DP));
        Ans=0;
        DP[N]=1;
        for(int i=N-1;i>=1;i--)
        {
            Set[1]=i;
            dfs( 1, i+1 );
            DP[i]=Ans;
        }
        printf("%d\n", DP[1]);
    }
    return 0;
}
並沒有多大效果...但是還是少了1s 不錯了