1. 程式人生 > >暴力搜尋(HDU 5305,Friends)

暴力搜尋(HDU 5305,Friends)

1<=n<=8,這個範圍的資料不僅可以考慮位壓縮更可以階乘級別的暴力列舉或搜尋了。

本題主要和邊有關,所以點位壓縮沒什麼好方法,邊位壓縮也沒什麼意義,而且邊是O(n^2)級別的也不可能壓。

那就考慮列舉,首先要思考最壞情況有多少種答案,粗略算一下,發現頂多也就C(8,4)*C(7,3)*C(6,2)*C(5,1)=183750,100組資料可以接受。(事實上高估了很多,畢竟階乘的東西差一點就差很多,更準確的計算應該是C(7,3)*C(6,2)*C(5,1)=2625左右)。

由於點邊之間的關係是一個拓撲結構,所以很難一對一地列舉到所有情況,只能通過搜尋加剪枝的方式,看看能不能用較低的搜尋量完成列舉。

我們已經知道有效的答案不是很多,接下來就該考慮如何列舉以減少無用的搜尋。

既然主要問題是邊二選一而不是點,那我們最好考慮列舉邊,然後以點為限制條件,為了儘量較少搜尋量,限制條件儘量用完,即一個點所連的邊的種類必須各佔一半。那就在搜尋過程中記錄以確定的點連邊的情況,然後列舉邊的選擇,一直暴搜就好了,事實上效果很好,暴搜的效率和邊的順序有關,相關的邊放在一起剪枝效果更大,比如可以按點順序來搜,不過都差不多了。

程式碼:

#include<stdio.h>
using namespace std;
const int maxn = 10;
const int maxm = 100;

int n,m;
int U[maxm],V[maxm];
int id[maxn],a[maxn],b[maxn];
int cnt;

void dfs(int cur)
{
    if(cur==m+1)
    {
        cnt++;
        return;
    }
    if(a[U[cur]]<id[U[cur]]&&a[V[cur]]<id[V[cur]])
    {
        a[U[cur]]++;
        a[V[cur]]++;
        dfs(cur+1);
        a[U[cur]]--;
        a[V[cur]]--;
    }
    if(b[U[cur]]<id[U[cur]]&&b[V[cur]]<id[V[cur]])
    {
        b[U[cur]]++;
        b[V[cur]]++;
        dfs(cur+1);
        b[U[cur]]--;
        b[V[cur]]--;
    }
}

void solve()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        id[i]=a[i]=b[i]=0;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",U+i,V+i);
        id[U[i]]++;
        id[V[i]]++;
    }
    for(int i=1;i<=n;i++) if(id[i]&1)
    {
        puts("0");
        return;
    }
    else id[i]/=2;
    cnt=0;
    dfs(1);
    printf("%d\n",cnt);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--) solve();
    return 0;
}