例题7-6 带宽 Bandwidth UVA - 140
这道题像是一道比较水的图论题,其实主要不是靠图论,而是考的全排列暴力法。
首先看到n<=8,那最多才会枚举8!次,才4万多点,每次枚举最坏情况下计算88=64次,则最坏复杂度是4000064=o(2.6*10^6),不会超时。其实剪枝也不需要了,但是为了学习一下剪枝的思想,所以在这个题上试验一下。考虑到如果某个节点距其图邻接点的最大距离已经大于当前的最小带宽,那么其实剩下的这个排列的其他节点都不需要枚举了,因为根据题意,结果一定会大于等于这个最大距离。此时,直接跳出二重循环即可。跳出多重循环的技巧:如果不想用goto,就在最底层循环里设置一个flag,所有层的循环都检查一下flag。如果有不满足条件的,就设置flag为1,之后每层循环检查到flag,就都会跳出了。
紫书上还给了另一个剪枝,我实在没想到,而且我用的哈希表的邻接矩阵存图,如果实现他的这个剪枝,还得再开一个数组存每个节点的邻接点个数。对这个剪枝有兴趣的可以自己查阅紫书对应例题。
其实,丝毫不考虑剪枝,提交上去运行时间也才0.04,考虑第一个剪枝,运行时间就已经0.00了,所以第二个考虑不考虑没啥影响。
下面上代码。
#include <bits/stdc++.h>
#define N 35
using namespace std;
int g[N][N],spot[N],stv[N];
vector<int> va;
void read_str(string s);
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
string s;
while (cin>>s&&s!="#") {
fill(g[0],g[0]+N*N,0);
fill(spot,spot+N,0);
fill(stv,stv+N,0);
read_str(s);
int ct=0,ans=10000000;
for (int i=0;i<N;i++)
if (spot[i]) stv[ct++]=i;
do {
int almx=0,flag=0;
for (int i=0;i<ct;i++) {
if (flag) break;
int mx=-1;
for (int j=0;j<ct;j++) {
if (flag) break;
if (stv[i]==stv[j]) continue;
if (!g[stv[i]][stv[j]]) continue;
mx=max(mx,abs(i-j));
if (mx>=ans) flag=1;
}
almx=max(almx,mx);
}
if (almx<ans) {
va.clear();
ans=almx;
for (int i=0;i<ct;i++)
va.push_back(stv[i]);
}
}while (next_permutation(stv,stv+ct));
if (!va.empty()) cout<<(char)(stv[0]+'A');
for (int i=1;i<(int)va.size();i++)
cout<<' '<<(char)(va[i]+'A');
cout<<" -> "<<ans<<endl;
}
return 0;
}
void read_str(string s) {
int st=0;
while (st<(int)s.size()) {
int ver=s[st]-'A';
spot[ver]=1;
st+=2;
while (s[st]!=';'&&st<(int)s.size()) {
int ajc=s[st]-'A';
spot[ajc]=1;
g[ver][ajc]=1;
g[ajc][ver]=1;
st++;
}
st++;
}
}