题目大意:给一个完全图,求最大割。
据说这是一个NP难的问题,我开始想的便是DFS+回溯,果然暴力搜索超时,后来参考别人的爆搜,进行了一些优化,竟然过了。用了400MS。可能有比较好的剪枝方法,可惜我没找到。这里主要笔记一下随机化算法,毕竟以前没有见到过。
爆搜代码:
#include <iostream> #include <cstring> using namespace std; int g[22][22]; int val[22]; int max_sum=0; int sum=0; void dfs(int n,int t) { int i,j; if(t==n) { if(sum>max_sum) max_sum=sum; return; } val[t]=0; int tmp=0; for(i=0;i<t;i++) { if(val[i]==1) tmp+=g[t][i]; } sum+=tmp; dfs(n,t+1); sum-=tmp; tmp=0; val[t]=1; for(i=0;i<t;i++) { if(val[i]==0) tmp+=g[t][i]; } sum+=tmp; dfs(n,t+1); sum-=tmp; } int main() { int n; int i,j ; cin>>n; for(i=0;i<n;i++) for(j=0;j<n;j++) cin>>g[i][j]; dfs(n,0); cout<<max_sum<<endl; }
后来参考别人的随机化算法,感觉相当经典:
就是将一个大的集合S分成两个集合S1和S2,然后从集合S中随机选择某个点,改变其所在所在集合,然后形成新的S1和S2,计算新的权值和。
这样经过多次的随机选择,能够得到近似最优解(往往能够得到最优解)。
#include <iostream> #include <cstdlib> #include <cstring> using namespace std; int g[22][22]; int val[22]; int max_sum=0; int sum=0; void random(int n) { int sum1=0; int sum2=0; int sum=0; int j =0; int i =0; for(j=0;j<100000;j++) { int r = rand()%n; sum1=0; sum2=0; if(val[r]==0) { val[r]=1; for(i=0;i<n;i++) if(val[i]==1) sum1+=g[r][i]; for(i=0;i<n;i++) if(val[i]==0) sum2+=g[r][i]; //cout<<sum1<<" "<<sum2<<endl; sum+=sum2-sum1; if(sum>max_sum) max_sum=sum; //cout<<max_sum<<endl; } else { val[r]=0; for(i=0;i<n;i++) if(val[i]==0) sum1+=g[r][i]; for(i=0;i<n;i++) if(val[i]==1) sum2+=g[r][i]; sum+=sum2-sum1; if(sum>max_sum) max_sum=sum; /* cout<<sum1<<" "<<sum2<<endl; if(sum1<sum2) max_sum+=sum2-sum1; cout<<max_sum<<endl; */ } } cout<<max_sum<<endl; } int main() { int n; int i,j ; cin>>n; for(i=0;i<n;i++) for(j=0;j<n;j++) cin>>g[i][j]; memset(val,0,sizeof(val)); random(n); }