题目描述
烦恼的设计师(来自SGOI)
春天到了,百花齐放,西湖公园里新设置了许多花坛,设计师想用不同的花摆出不同的图案以吸引游人,于是设计了各种图案并且在花圃中选好了要摆放的花。不幸的是负责搬运和摆放的工人因为临时有事,只将花放到花架上就匆匆离开了,并没有按照设计师原来的设计方案摆放,结果花坛杂乱不堪,设计师只好自己来调整花的位置。由于设计师通常从事脑力劳动,较少从事搬运和摆放花盆的体力工作,所以请你帮忙找出一种移动方法使工作量最小。
不同种类的花有不同的类型编号,虽然地球上花的种类很多,但因为公园里的花不超过1,000,000种,所以花的类型编号不超过1,000,000。另一方面,出于美学考虑,一个花坛里摆放的不同种类的花不超过3种,且不同种类的花的数量不可太接近,对于任意两种花,数量多的花的盆数至少是数量少的花的2倍。
花坛是正六边形的,共摆放有19盆花,每盆花都放在一个转盘上,转动一盆花下面的转盘,会使周围的6盆花顺时针或逆时针移动一个位置(但不可把花转到花坛外),称为一次操作。你的任务:用最少的操作使花坛由初始状态转化为符合设计图纸的目标状态。例如:
初始状态 目标状态
如图,只需将处于圆心位置的那盆花的转盘顺时针转动一个位置,红色的花就移动到了目标位置。
输入输出格式
输入格式:
输入文件共11行,1至5行描述花坛的初始状态,7至11行表示花盆应摆放的位置。中间以空行分隔,5行数字分别表示花坛的5个行,其中第1、5两行有3个整数,第2、4两行有4个整数,第3行有5个整数,表示每一行的花的类型,不同的数代表不同种类的花。
输出格式:
输出文件,一行,包含一个整数,即最少的操作数,数据保证20步之内有解。
输入输出样例
输入样例#1:
1 1 1
1 2 1 1
1 1 1 1 1
1 1 1 1
1 1 1
1 1 1
1 1 1 1
1 1 2 1 1
1 1 1 1
1 1 1
输出样例#1:
1
提示信息
题意描述:
给定两个正6边形的花坛,要求求出从第一个变化到第二个的最小操作次数以及操作方式。一次操作是:选定不在边上的一盆花,将这盆花周围的6盆花按照顺时针或者逆时针的顺序依次移动一个单位。限定一个花坛里摆放的不同种类的花不超过3种,对于任意两种花,数量多的花的盆数至少是数量少的花的2倍 。(这是 SGOI-8 的一道题)
解题分析:
首先确定本题可以用广度优先搜索处理,然后来看问题的规模。正6边形共有19个格子可以用来放花,而且根据最后一句限定条件,至多只能存在 C(2,19) * C(5,17) = 1058148 种状态,用搜索完全可行。然而操作的时候,可以预料产生的重复节点是相当多的,需要迅速判重才能在限定时间内出解,因此想到了哈希表。那么这个哈希函数如何设计呢?注意到19个格子组成6边形是有顺序的,而且每一个格子只有3种可能情况,那么用3进制19位数最大 3^20-1=3486784400,注意,这个数值大于2^31,但小于2^32。于是我们将每一个状态与一个整数对应起来,使用除余法就可以了。
算法使用:
哈希表,裸BFS(言简意赅!!!)
代码结构:
1.读入:
据题目中“花的类型编号不超过1,000,000。另一方面,出于美学考虑,一个花坛里摆放的不同种类的花不超过3种”可知,能以3进制数(long long)对每种可能的状态存储(类似离散化)
long long st=0,cnt=0,ed=0;//st为起始状态对应值,ed为目标状态,cnt为对应编号
for(int i=1;i<=19;i++)
{
int x;
cin>>x;
if(mm[x]==0)//未出现?
{
m[x]=cnt++;
mm[x]=1;
}
st*=3;
st+=m[x];
}
for(int i=1;i<=19;i++)
{
int x;
cin>>x;
ed*=3;
ed+=m[x];
}
2.广搜:
q.push(P(st,0));//q为队列,P是pair<int,int>宏定义后
while(!q.empty())//队列未空
{
P tmp=q.front();
q.pop();
if(tmp.first==ed)
{
cout<<tmp.second;
return 0;
}
int th_tmp=tmp.first;
for(int i=5;i>=1;i--)
{
for(int j=(i==5||i==1?3:(i==4||i==2?4:5));j>=1;j--)
{
a[i][j]=th_tmp%3;
th_tmp/=3;
}
}//将数值状态转成数组
for(int i=2;i<=4;i++)
{
for(int j=2;j<=(i==2||i==4?3:4);j++)
{
long long val1=round(i,j,1);//顺时针旋转(x,y,方向)
if(find(val1)==0) //哈希寻找
{
if(val1==ed)
{
cout<<tmp.second+1;
return 0;
}//重点!!!!!帮助减少冗余扩展(不然就会超时——时间卡得太严)
q.push(P(val1,tmp.second+1));//扩展
gethash(val1);
}
long long val2=round(i,j,-1);//逆时针
if(find(val2)==0)
{
if(val2==ed)
{
cout<<tmp.second+1;
return 0;
}
q.push(P(val2,tmp.second+1));
gethash(val2);
}
}
}
}
3.哈希:
int val[siz];
inline int f(long long x)
{
return x%siz;//取余法
}
inline void gethash(int x)
{
int number=(f(x)+siz)%siz;
while(val[number]!=x&&val[number]) number=(number==siz?0:number+1);//寻找没有冲突的位置
val[number]=x;//记录
}
inline int find(int x)
{
int number=(f(x)+siz)%siz;
while(val[number]!=x&&val[number]) number=(number==siz?0:number+1);
return val[number]==x;
}
4.旋转:
inline long long round(int x,int y,int f) {
int b[7][7];
for(int i=1; i<=5; i++)for(int j=1; j<=(i==5||i==1?3:(i==4||i==2?4:5)); j++)b[i][j]=a[i][j];
if(x==3) {//第三行
if(f==1)
{
swap(b[x-1][y],b[x-1][y-1]);
swap(b[x-1][y-1],b[x][y-1]);
swap(b[x][y-1],b[x+1][y-1]);
swap(b[x+1][y-1],b[x+1][y]);
swap(b[x+1][y],b[x][y+1]);
} else {
swap(b[x-1][y],b[x][y+1]);
swap(b[x][y+1],b[x+1][y]);
swap(b[x+1][y],b[x+1][y-1]);
swap(b[x+1][y-1],b[x][y-1]);
swap(b[x][y-1],b[x-1][y-1]);
}
} else if(x==2) {第二行
if(f==1) {
swap(b[x-1][y],b[x-1][y-1]);
swap(b[x-1][y-1],b[x][y-1]);
swap(b[x][y-1],b[x+1][y]);
swap(b[x+1][y],b[x+1][y+1]);
swap(b[x+1][y+1],b[x][y+1]);
} else {
swap(b[x-1][y],b[x][y+1]);
swap(b[x][y+1],b[x+1][y+1]);
swap(b[x+1][y+1],b[x+1][y]);
swap(b[x+1][y],b[x][y-1]);
swap(b[x][y-1],b[x-1][y-1]);
}
} else if(x==4) {第四行
if(f==1) {
swap(b[x-1][y],b[x][y-1]);
swap(b[x][y-1],b[x+1][y-1]);
swap(b[x+1][y-1],b[x+1][y]);
swap(b[x+1][y],b[x][y+1]);
swap(b[x][y+1],b[x-1][y+1]);
} else {
swap(b[x-1][y],b[x-1][y+1]);
swap(b[x-1][y+1],b[x][y+1]);
swap(b[x][y+1],b[x+1][y]);
swap(b[x+1][y],b[x+1][y-1]);
swap(b[x+1][y-1],b[x][y-1]);
}
}
long long ans=0;
for(int i=1; i<=5; i++) for(int j=1; j<=(i==5||i==1?3:(i==4||i==2?4:5)); j++)ans*=3,ans+=b[i][j];
return ans;
}
5.时间复杂度:
状态数约为700000
O(700000*19+700000*2*7*2*19)O(70000000)
是算法不加任何优化下的最优值——双端队列太麻烦,A*难写
在加上O(1),O(2)的情况下可以980毫秒通过:
6.完整代码:
#include<bits/stdc++.h>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
using namespace std;
typedef pair<int,long long> P;
int step[1000000];
int a[7][7],ans;
map<int,int> m;
map<int,bool> mm;
queue<P> q;
const int siz=1e7+7;
int val[siz];
inline int f(long long x) {
return x%siz;
}
inline void gethash(int x) {
int number=(f(x)+siz)%siz;
while(val[number]!=x&&val[number]) {
ans++;
number=(number==siz?0:number+1);
}
val[number]=x;
}
inline int find(int x) {
int number=(f(x)+siz)%siz;
while(val[number]!=x&&val[number]) {
ans++;
number=(number==siz?0:number+1);
}
return val[number]==x;
}
inline long long round(int x,int y,int f) {
int b[7][7];
for(int i=1; i<=5; i++)for(int j=1; j<=(i==5||i==1?3:(i==4||i==2?4:5)); j++)b[i][j]=a[i][j];
if(x==3) {
if(f==1) {
swap(b[x-1][y],b[x-1][y-1]);
swap(b[x-1][y-1],b[x][y-1]);
swap(b[x][y-1],b[x+1][y-1]);
swap(b[x+1][y-1],b[x+1][y]);
swap(b[x+1][y],b[x][y+1]);
} else {
swap(b[x-1][y],b[x][y+1]);
swap(b[x][y+1],b[x+1][y]);
swap(b[x+1][y],b[x+1][y-1]);
swap(b[x+1][y-1],b[x][y-1]);
swap(b[x][y-1],b[x-1][y-1]);
}
} else if(x==2) {
if(f==1) {
swap(b[x-1][y],b[x-1][y-1]);
swap(b[x-1][y-1],b[x][y-1]);
swap(b[x][y-1],b[x+1][y]);
swap(b[x+1][y],b[x+1][y+1]);
swap(b[x+1][y+1],b[x][y+1]);
} else {
swap(b[x-1][y],b[x][y+1]);
swap(b[x][y+1],b[x+1][y+1]);
swap(b[x+1][y+1],b[x+1][y]);
swap(b[x+1][y],b[x][y-1]);
swap(b[x][y-1],b[x-1][y-1]);
}
} else if(x==4) {
if(f==1) {
swap(b[x-1][y],b[x][y-1]);
swap(b[x][y-1],b[x+1][y-1]);
swap(b[x+1][y-1],b[x+1][y]);
swap(b[x+1][y],b[x][y+1]);
swap(b[x][y+1],b[x-1][y+1]);
} else {
swap(b[x-1][y],b[x-1][y+1]);
swap(b[x-1][y+1],b[x][y+1]);
swap(b[x][y+1],b[x+1][y]);
swap(b[x+1][y],b[x+1][y-1]);
swap(b[x+1][y-1],b[x][y-1]);
}
}
long long ans=0;
for(int i=1; i<=5; i++) for(int j=1; j<=(i==5||i==1?3:(i==4||i==2?4:5)); j++)ans*=3,ans+=b[i][j];
return ans;
}
int main() {
long long st=0,cnt=0,ed=0;
for(int i=1; i<=19; i++) {
int x;
cin>>x;
if(mm[x]==0) {
m[x]=cnt++;
mm[x]=1;
}
st*=3;
st+=m[x];
}
for(int i=1; i<=19; i++) {
int x;
cin>>x;
ed*=3;
ed+=m[x];
}
q.push(P(st,0));
while(!q.empty()) {
P tmp=q.front();
q.pop();
if(tmp.first==ed) {
cout<<tmp.second;
return 0;
}
int th_tmp=tmp.first;
for(int i=5; i>=1; i--) {
for(int j=(i==5||i==1?3:(i==4||i==2?4:5)); j>=1; j--) {
a[i][j]=th_tmp%3;
th_tmp/=3;
}
}
for(int i=2; i<=4; i++) {
for(int j=2; j<=(i==2||i==4?3:4); j++) {
long long val1=round(i,j,1);
if(find(val1)==0) {
if(val1==ed)
{
cout<<tmp.second+1;
return 0;
}
q.push(P(val1,tmp.second+1));
gethash(val1);
}
long long val2=round(i,j,-1);
if(find(val2)==0) {
if(val2==ed){
cout<<tmp.second+1;
return 0;
}
q.push(P(val2,tmp.second+1));
gethash(val2);
}
}
}
}
return 0;
}
另:实用小功能:ctrl+shift+A->压行