【题目链接】
UVa 140 Bandwidth
【问题描述】
给出一个$n$个结点的图G。 对一个结点的排列,定义结点的带宽为和其图中相邻结点在排列中的最远距离,定义排列的带宽为所有结点带宽的最大值。 给定图G,求出让带宽最小的结点排列。
【解题思路】
如果不考虑效率,本题完全可以递归,或使用next_permutation(),枚举全排列,分别计算带宽,然后选取最小的方案。 ~~虽然这样也能通过本题数据(->_->)~~,我们还是要进行优化。
剪枝。 本题并没有什么可行性约束,任何排列都是合法的。但我们可以考虑最优性剪枝。 记录下当前找到的最小带宽$best$,如果在搜索过程中发现已经有某个点的带宽大于该带宽,则该情况下说明无论如何扩展不出最优解,果断剪枝。 除此之外,如果在搜索到结点$u$时,$u$结点还有$m$个邻接点没有确定位置,对于$u$来说,最好的情况就是这$m$个点紧跟在u后面,这样,这$m$个点给$u$带来的结点带宽为$m$,而其他任何非理想的情况,带来的带宽至少为$m+1$。如果$m \ge best$,即在最好的情况下也无法得到比当前最优解更优的解,那么剪枝。
也就是说,剪枝的一个思路就是考虑当前的最理想方案,如果最理想方案也无法的到更优解,则剪枝。 IDA*的剪枝也是基于这个思想。
【AC代码】
强烈吐槽本题的输入格式。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <climits>
#define Id int
#define MAXN 8
#define MAXL 10000
int n;
bool G[MAXN][MAXN];
inline void addUEdge(int u, int v){
G[u][v] = G[v][u] = true;
}
int pos[MAXN];
Id a[MAXN], ans[MAXN];
int best;
inline void search(int cur, int bandwidth){
if(cur == n){
if(bandwidth < best) memcpy(ans, a, sizeof a), best = bandwidth;
}
else if(bandwidth > best) return;
else for(Id i = 0; i < n; i++) if(pos[i] == -1){
int max = bandwidth;
int count = 0;
a[cur] = i, pos[i] = cur;
for(Id j = 0; j < n; j++) if(G[i][j]){
if(pos[j] != -1) max = std::max(max, pos[i] - pos[j]);
else count++;
}
if(count >= best) break;
search(cur + 1, max);
a[cur] = -1, pos[i] = -1;
}
}
Id id[256];
char letter[MAXN];
inline void solve(){
best = INT_MAX;
memset(a, -1, sizeof a);
memset(pos, -1, sizeof pos);
search(0, 0);
for(int i = 0; i < n; i++){
printf("%c ", letter[ ans[i] ]);
}
printf("-> %d\n", best);
}
int main(){
char input[MAXL];
while(scanf("%s", input) == 1 && input[0] != '#'){
n = 0;
for(char ch = 'A'; ch <= 'Z'; ch++){
if(strchr(input, ch)){
id[ch] = n;
letter[n] = ch;
n++;
}
}
memset(G, 0, sizeof G);
int len = strlen(input);
char curCh = '\0';
for(int i = 0; i < len; i++){
char &ch = input[i];
if(curCh){
if(ch == ':') continue;
else if(ch == ';') curCh = '\0';
else addUEdge(id[curCh], id[ch]);
}
else curCh = ch;
}
solve();
}
return 0;
}
就这样啦