天梯赛 L3 - 015 球队“食物链” 【dfs + 剪枝】

传送门
题意不多说.

思路: 这个问题很明显的是哈密顿回路的版本, 而哈密顿回路是个NP问题, 用dfs当然可以出答案, 但是点不能太多, 这个题目20个点, 再加上剪枝就可以过了. 所以首先确定我们的想法就是dfs. 我们首先知道的是如果存在食物链, 那么肯定要从1开始搜是最优的. 其次是要字典序最小, 那么肯定下一个要遍历的点的编号尽量的小最好. 所以建好了图以后要对每个点可以到达的点排个序. 再就是如何剪枝了?那就是如果当前剩余的没有遍历过的点没有一个可以到达1,那么就可以直接不用搜了…..

AC Code

const int maxn = 20+5;
char s[maxn][maxn];
vector<int>g[maxn];
int flag = 0;
int vis[maxn], a[maxn][maxn];
int n, pre[maxn], ans[maxn];
void dfs(int u, int d) {
    if (flag) return ;
    if (d == n) {
        if (a[u][1]) flag = u;
        return ;
    }
    int f = 0;
    for (int i = 1 ; i <= n ; i ++) {  // 剪枝
        if (!vis[i] && a[i][1]) {
            f = 1;
            break;
        }
    }
    if (!f) return ;
    for (int i = 0 ; i < sz(g[u]) ; i ++) {
        int to = g[u][i];
        if (vis[to]) continue;
        pre[to] = u;
        vis[to] = 1;
        dfs(to, d+1);
        if (flag) return ;
        vis[to] = 0;
    }
}
void solve()
{
    cin >> n;
    for (int i = 1 ; i <= n ; i ++) {
        scanf("%s", s[i]+1);
    }
    for (int i = 1 ; i <= n ; i ++) {
        for (int j = 1 ; j <= n ; j ++) {
            if (i == j || s[i][j] == 'D') continue;
            if (s[i][j] == 'L' && !a[j][i]) {
                g[j].pb(i);
                a[j][i] = 1;
            }
            if (s[i][j] == 'W' && !a[i][j]) {
                g[i].pb(j);
                a[i][j] = 1;
            }
        }
    }
    for (int i = 1 ; i <= n ; i ++) {
        sort(g[i].begin(), g[i].end()); // 保证字典序最小
    }
    pre[1] = -1; vis[1] = 1;
    dfs(1, 1);
    if (!flag) cout << "No Solution" <else {
        int k = 0;
        while(flag != -1) {
            ans[++k] = flag;
            flag = pre[flag];
        }
        for (int i = k ; i >= 1 ; i --) {
            printf("%d%c", ans[i], i == 1?'\n':' ');
        }
    }
}

你可能感兴趣的:(简单学习日常)