题目链接
题目大意
叶良辰班里有n个人,他想让大家都知道他的大名,但是国庆节大家都回家了,他只能通过电话让自己出名:“你只需要记住,我叫叶良辰,把我的大名告诉你的同学,待我大名远扬,我叶良辰必有重谢!”
已知通一次电话需要1分钟,给定m对关系,每对关系中的两个人可以互相通话。
求:叶良辰闻名全班所需要的最短时间。
解题思路
先考虑单个人的行为:
假如我接到了电话,那么有哪些可能发生的情况呢?
1.我能联系到的同学都已经接到电话了:我不需要再做任何事;
2.我刚好有一个未接到电话的同学:我打个电话给他;
3.我有x个(x>1)未接到电话的同学:依次遍历x个同学,比如第一次选择A同学进行通知,当所有人都通知完毕后,吃一颗后悔药,第二次改变主意选择B同学进行通知。
现考虑整体的行为:
假如已经有x个人接到了电话,则在下一轮的打电话中,每个人都会遇到以上3种情况中的其中一种,如果是情况3,每个人都有吃后悔药的机会——实际上这是一个遍历的过程。
总的来说,算法过程如下:
做出选择->通知到所有人后记录最短时间->吃后悔药->重新做选择->再次通知到所有人后更新最短时间。
算法步骤
1) 初始化数据:
string s[MAXN]; //序号到名字的映射
map<string, int> mp; //名字到序号的映射
bool arc[MAXN][MAXN]; //邻接矩阵
bool vis[MAXN]; //访问标记
int Step = 999999999; //最小的步数
vector<int> q[MAXN]; //每一步中,已收到通知的列表
2) 将叶良辰加入第一次的队列q[0]中,以此为起点进行遍历。
3) 若所有人都收到了通知,更新ans,结束递归并返回步骤6。
否则,取出第i次的队列q[i]中的第j个人的序号,设为k。
4) 若k没有未收到通知的邻居,如果k是第i次最后做出选择的人,则进入第i+1次的通知,使i=i+1,进入步骤3;
如果k后面还有人没做出选择,则使j=j+1,进入步骤3。
5) 若k有未收到通知的邻居,则k选择一个邻居l进行通知,并标记已收到通知的人+1,将k以及l加入第i+1次的队列中。如果k是第i次最后做出选择的人,则进入第i+1次的通知,转入步骤3;如果k后面还有人没做出选择,则使j=j+1,转入步骤3。
6) 当递归返回时,执行遍历操作:将k以及l从第i+1次的队列中剔除,将l标记为未访问——这是一次吃“后悔药”的操作。
源程序
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#include<cstring>
using namespace std;
const int MAXN=12;
int n,m;
string s[MAXN]; //序号到名字的映射
map<string, int> mp; //名字到序号的映射
bool arc[MAXN][MAXN]; //邻接矩阵
bool vis[MAXN]; //访问标记
int Step = 999999999; //最小的步数
struct node //存放前一个结点的坐标p,到达本结点所需的步数step
{
int p,step;
};
vector<int> q[MAXN]; //每一步中,已收到通知的列表
node tmp[MAXN], ans[MAXN]; //分别存放计算的中间值和最终答案
void dfs(int i, int j, int num, int step)
{
if (num == n)
{
if (step < Step)
{
Step = step;
for (int i = 0; i < n; i++)
{
ans[i].p = tmp[i].p;
ans[i].step = tmp[i].step;
}
}
return;
}
int k = q[i][j];
bool flag = false;
for (int l = 0; l < n; l++) //q[i][j]选邻居
{
if (!vis[l]&&arc[k][l]) //选邻居l
{
flag = vis[l] = true;
q[i+1].push_back(k);
q[i+1].push_back(l);
tmp[l].p = k;
tmp[l].step = i+1;
if (j == q[i].size()-1)
dfs(i+1,0,num+1,tmp[l].step); //在第i次选邻居时,所有j都选了,则进入下一次(i+1),step保持最大的一个(最新)
else
dfs(i,j+1,num+1, tmp[l].step); //还有人没选,继续选
q[i+1].erase(q[i+1].end()-1); //退回,假设不选择邻居l,而选择下一个邻居
q[i+1].erase(q[i+1].end()-1);
vis[l] = false;
}
}
if (!flag) //没有邻居了q[i][j]
{
if (j == q[i].size()-1)
dfs(i+1,0,num,step);
else
dfs(i,j+1,num,step);
}
}
int main()
{
cin>>n>>m;
int num = 0;
memset(arc,0,sizeof(arc));
memset(vis,0,sizeof(vis));
while (m--)
{
string s1,s2;
cin>>s1>>s2;
if (mp.find(s1) == mp.end())
{
s[num] = s1;
mp[s1] = num++;
}
if (mp.find(s2) == mp.end())
{
s[num] = s2;
mp[s2] = num++;
}
arc[mp[s1]][mp[s2]] = arc[mp[s2]][mp[s1]] = true;
}
string start;
cin>>start;
if (n == 1)
{
cout<<'0'<<endl;
return 0;
}
int v = mp[start];
tmp[v].p = -1;
tmp[v].step = 0;
q[0].push_back(v);
vis[v] = true;
dfs(0,0,1,0);
cout<<Step<<endl;
for (int i = 1; i <= Step; i++)
{
int cnt = 0;
for (int j = 0; j < n; j++)
if (ans[j].step == i)
++cnt;
cout<<cnt<<endl;
for (int j = 0; j < n; j++)
if (ans[j].step == i)
cout<<s[ans[j].p]<<' '<<s[j]<<endl;
}
return 0;
}