N <=20,想到了状压。
f[i][j]表示在走过的点状态为i的情况下以j结尾的最大收获,pre[i][j]表示在前述状态下前一个点的编号。初始化pre数组为-1表示结束(我使用了0 - n-1作为节点的编号,使用1-n就不需要初始化)
状态转移方程
f[i][j] = max(f[i-1<<j][k]+f[1<<j][j]),((i-1<<j)&(1<<k)==1)&&graph[k][j]==1
遍历时每遍历到一个状态,使用该状态尝试更新后续所有状态,并且判断该状态是否大于当前答案。假如答案可以更新,则记录终点以及对应状态,便于后续遍历路径。
里面的两个坑,首先只有从上到下的路径,是个有向图;另外不一定从1号点开始,所以要先遍历一遍(其实如果用0号点表示虚拟源点就不需要这么麻烦orz是我太菜写复杂了)
代码
#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
const int N = 20, M = 1 << N;
int f[M][N], graph[N][N], pre[M][N];
int n, ans, e, state;
int main(){
cin>>n;
memset(pre, -1, sizeof pre);
for(int i = 0;i < n;i++){
cin>>f[1 << i][i];
}
for(int i = 0;i < n - 1;i++){
for(int j = i + 1;j < n;j++){
cin>>graph[i][j];
}
}
// for(int i = 0;i < n;i++){
// for(int j = 0;j < n;j++){
// cout<<graph[i][j]<<" ";
// }
// cout<<endl;
// }
for(int i = 0;i < n;i++){
if(ans < f[1 << i][i]){
e = i;
state = 1 << i;
ans = f[state][e];
}
}
for(int i = 1;i <= (1 << n);i++){
for(int j = 0;j < n;j++){
if(i >> j & 1){
for(int k = 0;k < n;k++){
if(graph[j][k] == 1 && !(i >> k & 1)){
if(f[i + (1 << k)][k] < f[i][j] + f[1 << k][k]){
f[i + (1 << k)][k] = f[i][j] + f[1 << k][k];
pre[i + (1 << k)][k] = j;
}
if(ans < f[i + (1 << k)][k]){
ans = f[i + (1 << k)][k];
e = k;
state = i + (1 << k);
}
}
}
}
}
}
stack<int> route;
while(e != -1){
route.push(e + 1);
int p = pre[state][e];
state -= 1 << e;
e = p;
}
while(route.size()){
cout<<route.top()<<" ";
route.pop();
}
cout<<endl<<ans<<endl;
return 0;
}