UVA_658

    这个问题纠结了比较久了,后来做另外一道题目的时候悠然告诉我说一般用二进制表示状态运算起来更快一些,这样一行就可以用一个数字去表示其状态了。按这个思路继续想下去就是如何建图了,其实无论邻接表还是邻接矩阵貌似建完整图的时间复杂度都比较大,而且好多状态未必用的到,因而不如干脆不建完整图,每次都扫描一遍转换规则,如果可以生成另一个状态就将另一个状态直接入队即可。

    这样写下来时间还是比较多,后来看了别人的解题报告,发现在判定当前状态是否可以转换的时候可以直接用位运算去操作:

    ①判定某些位置是否为1,如判定2、4位置为1,则转化为判断x|0101是否等于x。

    ②判定某些位置是否为0,如判定2、4位置为0,则转化为判断x&1010是否等于x。

    ③将某些位置转化为1,如2、4位置转化为1,则令x=x|0101。

    ④将某些位置转化为0,如2、4位置转化为0,则令x=x&1010。

    在用二进制表示状态的基础上采用这些位运算技巧之后,速度就变得比较快了。

#include<stdio.h>
#include<string.h>
#define MAXN 30
#define MAXM 110
#define MAXD 2000000
#define INF 1000000000
int s[2][MAXM], t[2][MAXM], w[MAXM], N, M;
char a[MAXN], b[MAXN];
int q[MAXD], inq[MAXD], d[MAXD];
int init()
{
int i, j;
scanf("%d%d", &N, &M);
if(!N && !M)
return 0;
memset(s, 0, sizeof(s));
memset(t, 0, sizeof(t));
for(i = 0; i < M; i ++)
{
scanf("%d%s%s", &w[i], a, b);
for(j = 0; j < N; j ++)
{
if(a[j] == '+')
s[1][i] += (1 << j);
if(a[j] != '-')
s[0][i] += (1 << j);
if(b[j] == '+')
t[1][i] += (1 << j);
if(b[j] != '-')
t[0][i] += (1 << j);
}
}
return 1;
}
void SPFA()
{
int i, j, k, MAX, front ,rear, u, v;
MAX = 1 << N;
for(i = 0; i < MAX; i ++)
{
d[i] = INF;
inq[i] = 0;
}
front = rear = 0;
d[MAX - 1] = 0;
q[rear ++] = MAX - 1;
while(front != rear)
{
u = q[front ++];
if(front > MAX)
front = 0;
inq[u] = 0;
for(i = 0; i < M; i ++)
if((u | s[1][i]) == u && (u & s[0][i]) == u)
{
v = u;
v |= t[1][i];
v &= t[0][i];
if(d[u] + w[i] < d[v])
{
d[v] = d[u] + w[i];
if(!inq[v])
{
q[rear ++] = v;
if(rear > MAX)
rear = 0;
inq[v] = 1;
}
}
}
}
if(d[0] == INF)
printf("Bugs cannot be fixed.\n");
else
printf("Fastest sequence takes %d seconds.\n", d[0]);
}
int main()
{
int T = 0;
while(init())
{
printf("Product %d\n", ++ T);
SPFA();
printf("\n");
}
return 0;
}


posted on 2011-10-25 10:21  Staginner  阅读(973)  评论(0编辑  收藏  举报