poj 1184 聪明的打字员

一开始直接bfs肯定是tle了,后来看了别人的题解(原始出处应该是NOI 01的题解)才找到一种绝妙的方法,就是把改变数字的位置与改变数字这俩个操作分离开来,时间复杂度从O(10^6)降到了O(6!*(2^k))(其中k <= 6)把改变这些数字的位置所能到达的状态和到达这些状态光标所经过的数字原始位置当成一个整体的状态(自己读起来都费力气,还是直接看代码吧,— —!),bfs预处理出到达这些状态最小耗费,然后枚举这些状态到达目标状态需要的耗费,注意其中有一些状态是无法到达目标状态的,即当前位的数字与目标状态该位数字不一样而且光标没有经过这位数字,该状态就无法到达目标状态,其他的状态只要把各个位与目标状态的数字差绝对值与预处理的耗费加起来即可,最小值即为答案.


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>
#include <map>
#include <string>
#include <climits> 
#include <set>
#include <string> 
#include <sstream>
#include <utility>
#include <ctime>

using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::istringstream;
using std::getline;
using std::make_pair;
using std::greater;

const int INFI((INT_MAX-1) >> 1);

struct NODE
{
	int arr[8];
	int dist;
};

bool vis[6][6][6][6][6][6][6][64]; //最后一维记录访问过的位置
int way[10000][9];		   //记录预处理出的状态,[0,5]表示各位数字的初始位置,[6]是光标位置(其实不需要记录),[7]是访问状态,[8]是耗费	
int count = 0;

NODE que[10000];
int front, back;

inline bool judge(int *sour)
{
	return vis[sour[0]][sour[1]][sour[2]][sour[3]][sour[4]][sour[5]][sour[6]][sour[7]];
}

inline void set1(int *sour)
{
	vis[sour[0]][sour[1]][sour[2]][sour[3]][sour[4]][sour[5]][sour[6]][sour[7]] = true;
}

void bfs()
{
	NODE cur, temp;
	memset(vis, 0, sizeof(vis));
	for(int i = 0; i < 6; ++i)
		cur.arr[i] = i;
	cur.arr[6] = 0;
	cur.arr[7]= 1;
	cur.dist = 0;
	front = 0;
	back = 1;
	que[0] = cur;
	set1(cur.arr);
	count = 0;
	while(front < back)
	{
		cur = que[front++];
		for(int i = 0; i < 8; ++i)
			way[count][i] = cur.arr[i];
//		way[count][7] = cur.state;
		way[count][8] = cur.dist;
		++count;
		temp = cur;
		swap(temp.arr[0], temp.arr[temp.arr[6]]);
		temp.arr[7] |= (1 << temp.arr[temp.arr[6]]);
		if(!judge(temp.arr))
		{
			temp.dist += 1;
			que[back++] = temp;
			set1(temp.arr);
		}
		temp = cur;
		swap(temp.arr[5], temp.arr[temp.arr[6]]);
		temp.arr[7] |= (1 << temp.arr[temp.arr[6]]);
		if(!judge(temp.arr))
		{
			temp.dist += 1;
			que[back++] = temp;
			set1(temp.arr);
		}
		if(cur.arr[6] > 0)
		{
			temp = cur;
			--temp.arr[6];
			temp.arr[7] |= (1 << (temp.arr[temp.arr[6]]));
			if(!judge(temp.arr))
			{
				temp.dist += 1;
				que[back++] = temp;
				set1(temp.arr);
			}
		}
		if(cur.arr[6] < 5)
		{
			temp = cur;
			++temp.arr[6];
			temp.arr[7] |= (1 << (temp.arr[temp.arr[6]]));
			if(!judge(temp.arr))
			{
				temp.dist += 1;
				que[back++] = temp;
				set1(temp.arr);
			}
		}
	}
}

int as[6], at[6];

int main()
{
	int s, t;
	bfs();
	while(~scanf("%d%d", &s, &t))
	{
		for(int i = 5; i >= 0; --i)
		{
			as[i] = s%10;
			at[i] = t%10;
			s /= 10;
			t /= 10;
		}
		int ans = INFI;
		int *tp;
		for(int i = 0; i < count; ++i)
		{
			int tans = 0;
			int j;
			tp = way[i];
			for(j = 0; j < 6; ++j)
				if(as[tp[j]] != at[j] && !(tp[7]&(1 << tp[j])))
					break;
				else
					tans += abs(as[tp[j]]-at[j]);
			if(j == 6)
			{
				tans += tp[8];
				if(tans < ans)
					ans = tans;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值