简单地偏分,艰难地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;
}