UVa140- Bandwidth
题意:给定n(n<=8)个节点的图和一个节点的排列,定义节点i的带宽为i和相邻节点在排列中的最远距离,整个图的带宽便是每个节点带宽的最大值,给定图G,求出让带宽最小的节点排列。
分析:刚开始做的时候,以为是最多26个节点,26的阶乘该是多大了…..然后以搜索剪枝的框架的方式写出来了,最后发现最多8个..,就算直接枚举效率也足够高,而且直接枚举肯定还会更简单,想必可以用next_permutation函数。
如果用搜索剪枝的方式的话,那么便考虑到优化的问题,如何剪枝呢?最优性剪枝就可以了,如果当前的最小带宽为ans,搜索过程中只要任意相邻节点的距离只要比ans大,那么便不会得到更优的解,所以可以剪枝了,紫书上还讲了一种剪枝,考虑当前的节点v,如果还有m个相邻点没有确定的话,那么最理想的情况m个点全部在v后面,这样距离最少也是m了,只要m大于等于ans,也可以剪枝了。
最优性剪枝时我们需要思考分析接下来的搜索且与当前的最优解比较,可以计算一下当前最理想情况可以得到怎么样的解,如果理想情况都无法得到更好的方案,那么便剪枝。
在这题中,数据很小很小,剪枝几乎没有起到任何作用,直接暴力就行了,总共才40320中排列……主要是字典序的问题,既然是要找字典序最小的方案,那么搜索枚举的时候以字典序升序的形式进行就好了。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <cstdlib>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
typedef unsigned char ch;
vector<ch> G[110];
set<ch> num;
char s[1010], res[30], perm[30];
int ans, sum;
int vis[110], mp[110];
void DFS(ch x, int dis)
{
if (dis == sum){
int m = 0;
for (set<ch>::iterator p = num.begin(); p != num.end(); p++)
for (unsigned int i = 0; i < G[*p].size(); i++) m = max(m, abs(mp[*p]-mp[G[*p][i]]));
if (ans > m){
ans = m;
memcpy(res, perm, sizeof(perm));
}
return ;
}
/*
剪枝的过程,这题没必要,感觉剪枝的判断相对来说也花了时间,还不如不剪枝
if (dis){
int siz = G[x].size(), cnt = 0, ma = 0;
for (int i = 0, cnt = 0; i < siz; i++){
ch c = G[x][i];
if (!vis[c]) cnt++;
else ma = max(ma, abs(mp[x]-mp[c]));
}
if (siz - cnt >= ans && ma >= ans) return ;
}*/
for (set<ch>::iterator p = num.begin(); p != num.end(); p++){
if (!vis[*p]){
perm[dis] = *p; mp[*p] = dis;
vis[*p] = 1;
DFS(*p, dis+1);
vis[*p] = 0;
}
}
}
int main()
{
while (gets(s), s[0] != '#'){
for (int i = 65; i <= 100; i++) G[i].clear();
int len = strlen(s);
ans = INF;
num.clear();
for (int i = 0; i < len; i++){
ch c = s[i++];
num.insert(s[i-1]);
while (i < len-1 && s[++i] != ';'){
ch t = s[i];
num.insert(t);
G[c].push_back(t);
}
}
memset(vis, 0, sizeof(vis));
sum = num.size();
DFS('a', 0);
for (int i = 0; i < sum; i++) printf("%c ", res[i]);
printf("-> %d\n", ans);
}
return 0;
}