您的位置:首页 > 其它

POJ2531-Network Saboteur-暴力枚举+记忆化/dfs/随机化乱搞

2016-01-24 15:29 435 查看
。。。题目没什么好说的。

把n个点 分配到两个集合,   求sum= A集合每个点到B集合所有点的边的权重和

n=20; 

。暴力的算法就直接枚举每种情况

1、暴力枚举复杂度 2^19 * 20*20  复杂度略大..TLE...加个剪枝去掉一半 也要跑1S   .POJ的数据太水。。

枚举的时候可以利用位运算,重复利用一下之前的结果(复杂度2^n * n ),不用每次枚举把所有的值都求和,大概也能跑200ms

2、dfs,没剪枝也能过2^20* n ...400ms,比枚举快多了(枚举干了很多不必要的加法...) 

剪枝:  把求A-B之间最大外部和转为求AB各自最小内部和...

有些大牛剪完枝直接就16MS了。。。

3、随机化乱搞... 每次随机改变1个节点从A到B 或B到A,for 一遍更新sum值,重复10W次,基本都ac了,当然和数据规模有关系啦....   参考别人的代码大概也是100+MS

关于bitmask。。之前一直是 遍历i的每一位...今天看别人的写法会 更快一些... 

暴力枚举+记忆化:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <iostream>
using namespace std;
double eps=0.000001;
int tm[25][25];
int set[25];
int vis[5+(1<<20)];
int f[5+(1<<20)];
int main()
{
int st,i,j,k;
int n;
cin>>n;
for (i=1;i<=n;i++)
{
for (j=1;j<=n;j++)
{
scanf("%d",&tm[i][j]);
}
}
int maxx=0;
int all=1<<(n)-1;

for (k=1;k<all;k++)
{
if (vis[k]) continue;
vis[k]=1;
vis[k^all]=1; //剪枝1/2,使得复杂度变成 2^(n-1)

//-k&k是求k的lowbit位(最低位边的1)
//k-lowbit(k)得到【A集合】的上一个状态
//那么当前状态的sum值f[k]=f[last]+当前新增的点x到B集合的sum-之前A集合到x的sum
int lowbit=-k&k;
int last=k-lowbit;
f[k]=f[last];

int x=0;    //j便是新增的点
while(lowbit)
{x++;lowbit>>=1;}

for (j=1;j<=n;j++)
{
if ((1<<(j-1))&last)//表示j已经在A集合
f[k]-=tm[x][j];
else		//j在B集合
f[k]+=tm[x][j];
}
f[k^all]=f[k];
if (f[k]>maxx)	maxx=f[k];
}
printf("%d\n",maxx);

return 0;
}


暴力枚举代码:  1S

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <iostream>
using namespace std;
__int64 inf=15;
double eps=0.000001;
int tm[25][25];
int set[25];
int vis[1<<20+5];
int main()
{
int st,i,j,k;
int n;
cin>>n;
for (i=1;i<=n;i++)
{
for (j=1;j<=n;j++)
{
scanf("%d",&tm[i][j]);
}
}
__int64 maxx=0;
int all=1<<(n)-1;

for (k=0;k<=all;k++)
{
if (vis[k]) continue;
vis[k]=1;
vis[k^all]=1; //剪枝1/2,使得复杂度变成 2^(n-1)
set[1]++;
for (i=1;i<=n;i++)
{
if (set[i]==2)
{
set[i]=0;
set[i+1]++;
}
else break;
}
__int64 sum=0;
for(i=1;i<=n;i++)
{

if (!set[i]) continue;
for (j=1;j<=n;j++)
{
if (!set[j])
sum+=tm[i][j];
}
}
if (sum>maxx)maxx=sum;
}
printf("%I64d\n",maxx);

return 0;
}


dfs:200ms

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <iostream>
using namespace std;
double eps=0.000001;
int tmd[25][25];
int set[25];
int maxx;
int n;
int dfs(int x,int sum)
{
if (x>n)
{
if (sum>maxx)
maxx=sum;
return 0;
}
int tmdp=0,i;set[x]=0;
for (i=1;i<=x;i++)
{
if (!set[i]) continue;
tmdp+=tmd[i][x];//不把x选入A,sum+=[x到A集的和]
}
dfs(x+1,sum+tmdp);
tmdp=0;
set[x]=1;
for (i=1;i<=x;i++)
{
if (set[i])continue;
tmdp+=tmd[i][x];  //把x选入A,sum+=[x到B集的和]
}

dfs(x+1,sum+tmdp);

}

int main()
{
int st,i,j,k;
int t;
cin>>n;
for (i=1;i<=n;i++)
{
for (j=1;j<=n;j++)
{
scanf("%d",&tmd[i][j]);
}
}
maxx=0;
dfs(1,0);

printf("%d\n",maxx);
return 0;
}


随机化乱搞:http://blog.csdn.net/sssogs/article/details/8221244
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: