P1171 售货员的难题

简单地偏分,艰难地AC系列。

这道题看上去很简单,直接爆搜就完事了。

不加任何剪枝的爆搜是40分(听人说的),加上一个最水的最优性剪枝就80分。

这个剪枝我没想到,如果加上去90分,是这样的:

如果当前距离剩下\(s\)条边还没走,如果这些边权值都为最小值1,加上去的答案还大于等于当前答案,剪枝。

最后10分挺难拿的,但是也有剪枝方法可以搞,方法是这样的:

我们把从同一个点出发的所有边按照权值从小到大排序,我们走路的时候就优先走权值小的。

然后我们就可以用上面的两个剪枝,注意:放在for新答案的时候,这里的剪枝不是continue而是return

因为我们已经排序好了,当前比较小的边不满足,后面比这条边更大的边肯定更不满足,所以直接return掐掉。

最后一个剪枝能优化1000ms左右,真的神奇!

代码:

#include<cstdio>
#include<algorithm>

const int maxn = 21;
const int INF = 1e9 + 7;
struct Nodes
{
    int to, weight;
} G[maxn][maxn];
int n;
int ans = INF, minback = INF;
int back[maxn];
bool vis[maxn];
bool cmp(const Nodes a, const Nodes b)
{
    return a.weight < b.weight;
}
int read()
{
    int ans = 0, s = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0', ch = getchar();
    return s * ans;
}
void dfs(int u, int res, int t)
{
    if(t == n)
    {
        ans = std::min(ans, res + back[u]);
    }
    for(int i = 2; i <= n; ++i)
    {
        if(!vis[G[u][i].to])
        {
            if(res + G[u][i].weight >= ans) return;
            if(res + n - t + minback >= ans) return;
            vis[G[u][i].to] = true;
            dfs(G[u][i].to, res + G[u][i].weight, t + 1);
            vis[G[u][i].to] = false;
        }
    }
}
int main()
{
    n = read();
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= n; ++j)
        {
            G[i][j].weight = read();
            G[i][j].to = j;
        }
    }
    for(int i = 2; i <= n; ++i)
    {
        back[i] = G[i][1].weight;
        minback = std::min(minback, G[i][1].weight);
    }
    for(int i = 1; i <= n; i++) std::sort(G[i] + 1, G[i] + n + 1, cmp);
    vis[1] = true;
    dfs(1, 0, 1);
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Garen-Wang/p/9565772.html