图的搜索 剪枝真是门学问。。剪好了快的可真不是一倍两倍
刚开始搜的思路有问题 TLE了 后来枚举点暴力搜了一发 两百多ms
由于查找时权值是不断增加的 所以直接找集合间最大权的话不方便设置return点
看disscuss发现有一大牛 建了两个数组 通过所有边权-两集合内部边权(去重) 得到答案 dfs的时候找最小内部边权即可 当前状态权值>当前最小内部边权时直接跳出 两个数组分别寸当前状态两个集合中所含的点 每加一个点分别往两边加 假设要将点u加入A集合 那么内部权值就要加上u与A中现存的所有点的边权 最后用所有边权-两集合最小内部边权就是最大集合间边权
代码如下:
//渣剪枝
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
bool vis[21];
int mp[21][21];
int n,mx;
void dfs(int id,int flow)
{
mx = max(mx,flow);
vis[id] = 1;
int i;
for(i = 1; i <= n; ++i)
{
if(vis[i]) flow -= mp[id][i];
else flow += mp[id][i];
}
for(i = id+1; i <= n; ++i)
{
dfs(i,flow);
vis[i] = 0;
}
}
int main()
{
int i,j;
scanf("%d",&n);
mx = 0;
memset(mp,0,sizeof(mp));
for(i = 1; i <= n; ++i)
{
for(j = 1; j <= n; ++j) scanf("%d",&mp[i][j]);
}
dfs(0,0);
printf("%d\n",mx);
return 0;
}
//大牛剪枝
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int l[21],r[21];//左右集合
int mp[21][21];
int n,cut;//cut--内部边权
void dfs(int k,int flow,int in,int out)
{
if(flow >= cut) return;//当前集合内边权已比最小集合内边权大 return
int i,j,s;
s = 0;
for(i = 1; i <= in; ++i)//当前点与左集合各点边权
{
s += mp[k][l[i]];
}
if(k == n)
{
cut = min(cut,flow+s);
}
else
{
l[in+1] = k;
dfs(k+1,flow+s,in+1,out);
}
s = 0;
for(i = 1; i <= out; ++i)//当前点与右集合各点边权
{
s += mp[k][r[i]];
}
if(k == n)
{
cut = min(cut,flow+s);
}
else
{
r[out+1] = k;
dfs(k+1,flow+s,in,out+1);
}
}
int main()
{
int i,j,sum = 0;
scanf("%d",&n);
for(i = 1; i <= n; ++i)
{
for(j = 1; j <= n; ++j)
{
scanf("%d",&mp[i][j]);
sum += mp[i][j];
}
}
sum /= 2;//两两点间边权(去重)
cut = INF;//初始集合内边权最大
l[1] = 1;
dfs(2,0,1,0);
printf("%d\n",sum-cut);
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-16 03:42:01