題目大意:給一個超級立方體(具體看題目中的圖),16個點,每個點有4個相鄰點,圖中有標識。現在每個點安一個燈泡。一共16個燈泡,有8盞亮8盞滅,現在可以交換任意相鄰的2盞狀態不同的燈的狀態。求最少多少步能使編號1-8的8個燈泡亮,其他的滅。
題目分析:題目要求3步以內就夠了。16個點,每個點有4個相鄰點,16^4,不過case有13000+個。
可以考慮IDA*。將16個燈泡壓縮成一個整數,根據圖可以將每個點的相鄰點保存下來。直接搜就可以了。
啓發函數很顯然,就是高8位1的個數,因爲要保證高8位所有的數爲0,只要高8位有一個1,那麼至少要一步才能將其交換,所以高8位有多少1就至少要多少步。
其實不要這個啓發函數其實也是可以過的。
不過寫的時候卡在了按位與操作上了。。。還是狀態壓縮寫少了。。。
詳情請見代碼:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100000;
bool flag[N];
bool ok;
int ans;
int adj[16][4] =
{
{1,2,4,8},
{0,3,5,9},
{0,3,6,10},
{1,2,7,11},
{0,5,6,12},
{1,4,7,13},
{2,4,7,14},
{3,5,6,15},
{0,9,10,12},
{1,8,11,13},
{2,8,11,14},
{3,9,10,15},
{4,8,13,14},
{5,9,12,15},
{6,10,12,15},
{7,11,13,14},
};
int A(int x)
{
int i;
int ret = 0;
for(i = 15;i >= 8;i --)
{
ret += (bool)(x&(1<<i));
}
return ret;
}
void dfs(int cur,int dp)
{
if(cur == 255)
{
ok = true;
return;
}
if(ok)
return;
if(dp + A(cur) > ans)
return;
int i,j,k;
for(i = 15;i >= 0;i --)
{
if(cur&(1<<i))
{
for(j = 0;j < 4;j ++)
{
int tmp = 15 - adj[15-i][j];
if(((cur>>tmp)&1)^((cur>>i)&1))//!!!!!
{
int tp = cur;
tp ^= (1<<i);
tp ^= (1<<tmp);
if(flag[tp] == false)
{
flag[tp] = true;
dfs(tp,dp + 1);
flag[tp]= false;
}
}
}
}
}
}
int main()
{
int t,i,cas = 0;
scanf("%d",&t);
int a[20];
while(t --)
{
printf("Case #%d: ",++cas);
for(i = 0;i < 16;i ++)
scanf("%d",a + i);
int tmp = 0;
for(i = 0;i < 8;i ++)
tmp += a[i];
if(tmp > 3)
{
printf("more\n");
continue;
}
tmp = 0;
for(i = 15;i >= 0;i --)
{
if(a[i])
tmp |= (1<<(15 - i));
}
ans = -1;
while(1)
{
ans ++;
ok = false;
flag[tmp] = true;
dfs(tmp,0);
flag[tmp] = false;
if(ok)
break;
if(ans >= 3)
{
ans ++;break;
}
}
if(ans > 3)
puts("more");
else
printf("%d\n",ans);
}
return 0;
}