//#include <bits/stdc++.h>
#include <iostream>
using namespace std;
const int N=25;
int G[N][N];
int vis[N];
int res=-1;
int n;
void dfs(int x,int sum){
if(x==n+1){
res=max(res,sum);
return;
}
int m=0;//在sum的基础上,将x节点放在S/T集合能为 割的和 贡献多少
vis[x]=1;
for(int i=1;i<=x;i++){
if(!vis[i])m+=G[i][x];
}
dfs(x+1,sum+m);
m=0;
vis[x]=0;
for(int i=1;i<=x;i++){//只枚举到了前x个节点,只要考虑这些节点与x的边的影响
if(vis[i])m+=G[i][x];
}
dfs(x+1,sum+m);
//非常标准的爆搜了,七段码那题第一次碰到
//依次处理每个节点的状态,由该种状态延续下去枚举其余各点的状态
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>G[i][j];
}
}
dfs(1,0);//从第一个节点开始,逐个将各个节点选择放入某集合(不是先假设某个节点在某个集合噢,一开始假设所有节点都不在集合内
vis[1]=1;
dfs1(1,0,1);
cout<<res;
return 0;
}
下面这种不看也罢
//#include <bits/stdc++.h>
#include <iostream>
using namespace std;
const int N=25;
int G[N][N];
int vis[N];
int res=-1;
int n;
void dfs(int x,int sum){
if(x==n+1){
res=max(res,sum);
return;
}
int m=0;//在sum的基础上,将x节点放在S/T集合能为 割的和 贡献多少
vis[x]=1;
for(int i=1;i<=x;i++){
if(!vis[i])m+=G[i][x];
}
dfs(x+1,sum+m);
m=0;
vis[x]=0;
for(int i=1;i<=x;i++){
if(vis[i])m+=G[i][x];
}
dfs(x+1,sum+m);
//非常标准的爆搜了,七段码那题第一次碰到
//依次处理每个节点的状态,由该种状态延续下去枚举其余各点的状态
}
void dfs1(int x,int sum,int cnt){
for(int i=1;i<=n;i++){//更改x状态,对割线总和的影响
if(!vis[i])sum+=G[i][x];
else{
sum-=G[i][x];
}
}
// 改变x的状态,需要遍历所有节点,和x处于不同集合的节点,要加上边
// 现在处于相同集合的节点,需要减去和x的边,因为原来加过了和x的边
if(res<sum){
res=sum;
}//不return噢,修改可以继续
if(cnt==n)return;
for(int i=x+1;i<=n;i++){
cnt++;
vis[i]=1;
dfs1(i,sum,cnt);
cnt--;
vis[i]=0;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>G[i][j];
}
}
// 划分成二部图(设两个集合分别为S、T),使所有割的和最小
// 假设一开始所有节点都在S集合,通过dfs爆搜枚举将某些节点放入
// T集合,比较各种情况下 割的和 的大小,得到最大结果
// dfs(1,0);//从第一个节点开始,逐个将各个节点选择放入某集合(不是先假设某个节点在某个集合噢,一开始假设所有节点都不在集合内
// 其实,以上dfs就是爆搜的精炼写法,即逐个枚举所有节点的不同状态
// (即一开始所有节点都不在集合中,只考虑加入节点对结果的影响)
// 下面就是,假设所有节点都已经全部同时在S集合中,逐渐更改节点的状态
vis[1]=1;
dfs1(1,0,1);
cout<<res;
return 0;
}