题目:https://vjudge.net/problem/UVA-140
解题思路:
1.全排列:使用库函数next_permutation(a,a+n)
2.最优性剪枝:如果目前已经找到的最小带宽是k,若在新的一组排列中,发现已经有两个结点的距离大于或等于k,应强制把它“剪”掉,即剪枝。
3.全排列时肯定要对输入中出现的字母所对应的int数组做全排列,所以要对出现的字母做标记!
(1)首先A~Z每个字母在读入的字符数组s[]中遍历,查找是否出现过,num初始为0
若出现,letter[num]=ch; id[ch]=num++;
则可对s[]中出现的所有字母(不重复)按照字典序从小到大编号,即id
(2)vector<int> u,v;
遍历s[],将 :前面的字母对应的id加入u中, :后面的字母对应的id加入v中,且这两个操作同时进行
(3)p[]输出初始化为p[i]=i; //p[]记录每次全排列后对应的id序列
每次pos[p[i]]=i; //pos[]记录id(id=p[i])在全排列后的序列中的位置
(举例:p[]第一全排列即初始化后为:{0,1,2,3,4,5,6}
pos[p[5]]=pos[5]=5; pos[p[6]]=pos[6]=6; //id=5在位置5,di=6在位置6
p[]第二次全排列后为:{0,1,2,3,4,6,5}
pos[p[5]]=pos[6]=5; pos[p[6]]=pos[5]=6;//id=6在位置5,id=5在位置6
因为id对应着字母,所以可以知道两个字母之间的带宽,即abs(pos[u[i]]-pos[v[i]])
)
不断更新最优的带宽对应的全排列id序列,用memcpy(ansp,p,sizeof(p))比数组一个一个赋值要快
执行next_permutation(p,p+num);
把样例带进入执行一遍就懂了!
ac代码:
#include <iostream>
#include <cmath>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#define maxn 10
#define inf 0x3fffffff
using namespace std;
typedef long long ll;
int id[100];//字母对应的编号,(int)'Z'=90
char s[500],letter[30];
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
while(scanf("%s",s))
{
if(s[0]=='#') break;
int p[maxn],pos[maxn],ansp[maxn];
int num=0,len=(int)strlen(s);
for(char ch='A';ch<='Z';ch++)
if(strchr(s,ch))
{
letter[num]=ch;//若知道字母的id编号,则可由letter数组获取该字母,以便输出
id[ch]=num++;
}
vector<int> u,v;//u[i]与v[i]之间有边,u[i]和v[i]都是记录id编号
for(int i=0;i<len;i++)
{
int t=i;
i+=2;//i指向:后面的第一位
while(i<len && s[i]!=';')
{
u.push_back(id[s[t]]);
v.push_back(id[s[i]]);
i++;
}
}
int width=0,ans=inf;
for(int i=0;i<num;i++) p[i]=i;
do{
width=0;
for(int i=0;i<num;i++) pos[p[i]]=i;
for(int i=0;i<u.size();i++)
{
width=max(width,abs(pos[u[i]]-pos[v[i]]));
if(width>=ans) break;//剪枝
}
if(width<ans)
{
ans=width;
memcpy(ansp,p,sizeof(p));
}
}while(next_permutation(p,p+num));
for(int i=0;i<num;i++)
printf("%c ",letter[ansp[i]]);
printf("-> %d\n",ans);
}
return 0;
}