题意:给你一个图,图中点之间会有边权,现在问题是把图分成两部分,使得两部分之间边权之和最大。
目前我所知道的有四种做法:
方法一:状态压缩
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> /* 用状态压缩枚举,297ms。 题意:给你一个图,图中点之间会有边权,现在问题是把图分成两部分,使得两部分之间边权之和最大。 情况是对称的,也就是说枚举所有的分类情况中,有一半是冗余的。 举个例子来说,有5个节点,1、2、3分配为A组,4、5分配给B组, 这个分类的策略所计算的结果和1、2、3分配为B组,4、5分配给A组是一样的。 因此对称的就不需要再考虑了,如状态压缩时101和010,只要考虑其中一个就可以了。 至于当枚举到一个状态时,怎么判断它的对称状态已经枚举过了,采用异或就可以了。 */ using namespace std; const int maxn=21; int n; int w[maxn][maxn]; //int vis[maxn]; int visit[550000*2]; int ans=0; int set1[maxn]; int set2[maxn]; int idx1,idx2; int main() { int l; //cout<<(1<<20)<<endl; cin>>n; memset(w,0,sizeof(w)); for(int i=1;i<=n;i++){ for(int j=1;j<=i;j++) scanf("%d",&l); for(int j=i+1;j<=n;j++){ scanf("%d",&l); w[i][j]=w[j][i]=l; } } memset(vis,0,sizeof(vis)); memset(visit,0,sizeof(visit)); int all=1<<n-1; ans=0; //用状态压缩来枚举两组的情况 for(int i=1;i<(1<<n)-1;i++){ //对称的就不需要再考虑了,如101和010,只要考虑其中一个就可以了,用异或即可求出对称的状态。 if(!visit[all^i]){ visit[i]=1; idx1=idx2=0; //将编号存入两个数组中去,而不是用vis标记,从原本的1000+ms减少到297ms for(int j=0;j<n;j++){ if(i&(1<<j)) set1[idx1++]=j+1; else set2[idx2++]=j+1; } int tot=0; for(int k=0;k<idx1;k++){ for(int t=0;t<idx2;t++){ tot+=w[set1[k]][set2[t]]; } } if(tot>ans) ans=tot; } } printf("%d\n",ans); return 0; }