原题链接: http://acm.hdu.edu.cn/showproblem.php?pid=1689
一:题意
组成手串,给N个珠子,珠子之间有M对关系,有关系的可以相邻,开头与末尾算相邻,能否求出一个手串长度满足:
长度最短;
长度最小为3;
长度是奇数;
二:分析
以每一个珠子为起点,开始bfs,比较难理解的是vis标记数组,它的二维表示的是奇偶。
例如:vis[ k ][ 0 ]表示在当前路线,以k结尾的长度是偶数;若是vis[ k ][ 1 ],长度就是奇数。
看别人的解题报告时,还是有些疑问的,这里记录下。
一共有四种情况:
1:在一条路线上遇见两个k值,且节点处的奇偶相同
这种情况已经被vis标记,直接结束了,不用讨论了。
2.在一条路线上遇见两个k值,且奇偶不同
奇偶不同,代表第二次出现的k还是要被加入到队列,因为vis并没有标记,但是按常理来说,珠子只能用一次,在这里应该及时停止入队的啊,这是算法的小缺陷,大家可以想一下,两个相同k之间的节点还是会走一遍,到时又遇到第三个k,停止,浪费了很多资源
3.在两条线各遇见一个k值,且节点数奇偶一样
k一样,奇偶一样,vis标记,第二次遇见k的这个节点被放弃,不入队,但是这样是否正确,是正确的。因为第二次遇见k时,整个路线长度才刚刚和第一次遇见k的路线长度相等,这是为何,我在前面的博客介绍过,就是队列的step值极大差最大只能是1。
4.在两条线各遇见一个k值,奇偶不同
这就不用说了,各不影响,主要是上面的三种情况。
三:AC代码
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<vector>
using namespace std;
#define N 1005
const int inf = 0x7fffffff;
vector<int>g[N];
int vis[N][2];
struct node
{
int u, t;//t是珠子数
};
int bfs(int s)
{
int i, u, v;
queue<node >q;
node cur, next;
cur.u = s;
cur.t = 1;
memset(vis, 0, sizeof(vis));
vis[s][1] = 1; //奇数点,用一个珠子
q.push(cur);
while (!q.empty())
{
cur = q.front();
q.pop();
u = cur.u;
for (i = 0; i < g[u].size(); i++)
{
next.u = v = g[u][i];
next.t = cur.t + 1;
if (v == s&&next.t % 2 == 0 && next.t > 3) //结点处到达两次,故应该减一
return next.t - 1;
if (!vis[v][next.t % 2])
{
vis[v][next.t % 2] = 1;
q.push(next);
}
}
}
return inf;
}
int main()
{
int i, n, m, u, v, tt, cnt = 0;
scanf("%d", &tt);
while (tt--)
{
scanf("%d%d", &n, &m);
for (i = 1; i <= n; i++)
g[i].clear();
while (m--)
{
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
int ans = inf;
for (i = 1; i <= n; i++)
ans = min(ans, bfs(i));
if (ans == inf)
printf("Case %d: Poor JYY.\n", ++cnt);
else
printf("Case %d: JYY has to use %d balls.\n", ++cnt, ans);
}
return 0;
}