#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,p[25][25],q[25][25],s,ans;
bool v[25]; //需要确定每个女运动员是否已匹配。
void dfs(int x)
{
if (x > n)
{
ans = max(s,ans);
return;
}
for (int i = 1;i <= n;i++)
if (!v[i])
{
v[i] = 1;
s += p[x][i] * q[i][x];
dfs(x + 1);
s -= p[x][i] * q[i][x];//不同方案需回溯。
v[i] = 0;
}
}
int main(){
cin >> n;
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
scanf("%d",&p[i][j]);
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
scanf("%d",&q[i][j]);
dfs(1);
cout << ans;
return 0;
}
我们可以通过剪枝来 AC 后面两个点,可以剪掉已经不可能的情况:就是在任何一种搜索中的情况下的竞赛优势理论最大值(不一定可以配出此情况,但是理论最大值肯定比实际最大值要大,所以如果理论最大值都没有已搜过的完整路的答案大,实际上按这条路搜下去必定无法更新答案,就是无意义的路径)+原来已搜部分仍然没有之前搜出的答案大,说明这条路已经不可能了,直接 return 返回。
```cpp
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,p[25][25],q[25][25],maxx[25],s,ans;
bool v[25];
void dfs(int x)
{
int sum = s;
for (int i = x;i <= n;i++)
{
int maxx = 0;
for (int j = 1;j <= n;j++)
maxx = max(maxx,p[i][j] * q[j][i]);
sum += maxx;
}//求出当前选择的理论最大值。
if (sum <= ans) return;//如果理论最大值都达不到原来的答案,直接返回。
if (x > n)
{
ans = max(s,ans);
return;
}
for (int i = 1;i <= n;i++)
if (!v[i])
{
v[i] = 1;
s += p[x][i] * q[i][x];
dfs(x + 1);
s -= p[x][i] * q[i][x];
v[i] = 0;
}
}
int main(){
cin >> n;
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
scanf("%d",&p[i][j]);
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
scanf("%d",&q[i][j]);
dfs(1);
cout << ans;
return 0;
}
```
运行时间最长的测试点用时 $11ms$,可以通过。
但是(重点来啦),还可以优化。
直接在原来基础上加一个理论最大值的后缀和,预处理从男运动员 $x$ 到 $n$ 配的女运动员的竞赛优势的理论最大值。
于是我们又有了更快的 $AC$ 代码:
```cpp
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,p[25][25],q[25][25],maxx[25],s,ans;
bool v[25];
void dfs(int x)
{
int sum = s;
sum += maxx[x];//已经预处理过,直接使用数组。
if (sum <= ans) return;
if (x > n)
{
ans = max(s,ans);
return;
}
for (int i = 1;i <= n;i++)
if (!v[i])
{
v[i] = 1;
s += p[x][i] * q[i][x];
dfs(x + 1);
s -= p[x][i] * q[i][x];
v[i] = 0;
}
}
int main(){
cin >> n;
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
scanf("%d",&p[i][j]);
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
scanf("%d",&q[i][j]);
for (int i = n;i >= 1;i--)
{
for (int j = 1;j <= n;j++)
maxx[i] = max(maxx[i],p[i][j] * q[j][i]);
maxx[i] += maxx[i + 1]; //用一个数组预处理理论最大值的后缀和。
}
dfs(1);
cout << ans;
return 0;
}
```
运行最长时间的测试点仅仅 $5ms$,这就是剪枝+后缀和的威力。
本人第一次发题解,如有不足之处望大家理解指正。