2011 ACM/ICPC 北京赛区现场赛解题:Peach Blossom Spring

来源:互联网 发布:go语言编程 mobi 编辑:程序博客网 时间:2024/04/30 02:00

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4085


在做这道题之前,建议先看下POJ3123,相关的信息在这里和这里。


说一下这道题的思路吧。

首先判断无解情况,这个很简单,把两个点集做一次二分图匹配看是否全部连通就OK了。

之后用Minimal Steiner Tree那种DP思想求解出dp[i][j]所有结果,其中i是用二进制状态压缩的点集,j是点,dp[i][j]是j到点集的最短路。

最后,用map初始化两个点集中子集相连的最短路,DP求所有点的最短路。


代码

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#include <map>#define V 55#define A 11#define inf 0x3f3f3f3fusing namespace std;int n, m, k;int vis[V], id[A], per[6];int d[V][V], ans[1 << A][V];map <int, int> mp;bool mat[A][A];bool visit[A];int match[A];int dig(int x){    int cnt = 0;    while (x)    {        if (x % 2 == 1)            cnt++;        x >>= 1;    }    return cnt;}void steiner(int n, int a){    int i, j, k, mx, mk, top = (1 << a);    for (k = 0; k < n; ++k)    {        for (i = 0; i < n; ++i)            for (j = 0; j < n; ++j)                if (d[i][j] > d[i][k] + d[k][j])                    d[i][j] = d[i][k] + d[k][j];    }    for (i = 0; i < a; ++i)    {        for (j = 0; j < n; ++j)            ans[1 << i][j] = d[j][id[i]];    }    for (i = 1; i < top; ++i)    {        if (0 == (i & (i - 1)))            continue;        memset(vis, 0, sizeof(vis));        for (k = 0; k < n; ++k)        {            ans[i][k] = inf;            for (int sub = i; sub; sub = (sub - 1) & i)                if (ans[i][k] > ans[sub][k] + ans[i - sub][k])                    ans[i][k] = ans[sub][k] + ans[i - sub][k];        }        for (j = 0; j < n; ++j)        {            mx = inf;            for (k = 0; k < n; ++k)                if (ans[i][k] <= mx && 0 == vis[k])                    mx = ans[i][mk = k];            for (k = 0, vis[mk] = 1; k < n; ++k)                if (ans[i][mk] > ans[i][k] + d[k][mk])                    ans[i][mk] = ans[i][k] + d[k][mk];        }        if (dig(i % (1 << (a / 2))) == dig(i >> (a / 2)))        {            int mina = inf;            for (int j = 0; j < n; ++j)                mina = min(mina, ans[i][j]);            mp[i] = mina;        }    }}bool find(int x, int n){    for (int i = 0; i < n; ++i)    {        if (mat[x][i] && !visit[i])        {            visit[i] = true;            if (match[i] == -1 || find(match[i], n))            {                match[i] = x;                return true;            }        }    }    return false;}int hungary(int n){    memset(match, -1, sizeof(match));    int maxi = 0;    for (int i = 0; i < n; ++i)    {        memset(visit, false, sizeof(visit));        if (find(i, n))            maxi++;    }    return maxi++;}int main(){    int t;    scanf("%d", &t);    while (t--)    {        scanf("%d%d%d", &n, &m, &k);        for (int i = 0; i < n; ++i)        {            for (int j = 0; j < n; ++j)                d[i][j] = inf;            d[i][i] = 0;        }        for (int i = 0; i < m; ++i)        {            int u, v, w;            scanf("%d%d%d", &u, &v, &w);            d[u - 1][v - 1] = min(d[u - 1][v - 1], w);            d[v - 1][u - 1] = min(d[v - 1][u - 1], w);        }        for (int kk = 0; kk < n; ++kk)        {            for (int i = 0; i < n; ++i)                for (int j = 0; j < n; ++j)                    if (d[i][j] > d[i][kk] + d[kk][j])                        d[i][j] = d[i][kk] + d[kk][j];        }        memset(mat, false, sizeof(mat));        for (int i = 0; i < k; ++i)            for (int j = 0; j < k; ++j)            {                mat[i][j] = (d[i][n - j - 1] == inf ? false : true);            }        if (hungary(k) != k)            puts("No solution");        else        {            mp.clear();            for (int i = 0; i < k; ++i)                id[i] = i;            for (int i = 0; i < k; ++i)                id[k + i] = n - i - 1;            steiner(n, 2 * k);            map<int, int> :: iterator i, j;            for (i = mp.begin(); i != mp.end(); i++)            {                for (j = i, j++; j != mp.end(); ++j)                {                    if ((i -> first & j -> first) == 0)                    {                        mp[i -> first | j -> first] = min(mp[i -> first | j -> first], i -> second + j -> second);                    }                }            }            printf("%d\n", mp[(1 << (2 * k)) - 1]);        }    }    return 0;}