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;
}


你可能感兴趣的:(poj 1184 聪明的打字员)