【最短路\拆点】跳舞鸡

第3题:跳舞鸡

 

【题目描述】

从前有只鸡喜欢跳舞。为了跳出最美丽的舞蹈,它发明了一种机器。这个机器有N*N个格子。一个格子要么写着’+’,’-‘,要么写着0-9的数字。并且保证每个符号的上下左右四个方向都是数字,每个数字的上下左右四个方向都是符号。这样的机器有个神奇的功能:从一个写着数字的格子出发,每次走到一个相邻的格子,把走过格子的字符按先后顺序从左到右排过来,那么可以构成一个计算式。这只鸡决定当跳到当计算式的答案等于某个值的时候就停止。但是这只鸡的IQ实在太低了,它发现怎么跳也跳不完……于是让你帮它求一个最短的路径出来。如果有多种方案,输出字典序最小的那一个(注意’+’,’-’也要计入字典序的比较)。

输入数据保证有解。

 

【输入格式】

第一行一个正整数T,代表有T组数据。

对于每组数据,第一行两个正整数N和C。然后接下来N行是一个N*N的矩阵,代表这个机器的样子。矩阵过后一行是C个正整数,代表每次要达到的值L。

 

【输出格式】

对每组数据,输出C行字符串,为达到每个给出的值所需的最短且字典序最小的计算式。

 

【数据范围】

对于30分的数据,N小于等于10。C小于等于20。L小于等于50。

对于100分的数据,N小于等于20,T小于等于6。C小于等于50。L大于等于1小于等于250。

 


【输入样例】

2

5 3

2+1-2

+3-4+

5+2+1

-4-0-

9+5+1

20 30 40

3 2

2+1

+4+

5+1

2 20

【输出样例】

1+5+5+9

3+4+5+9+9

4+9+9+9+9

2

5+5+5+5


因为要求最短路,但是又要保证路径中的权值为定值,所以考虑拆点,把一个点拆成750个点(-250~500),表示走到(i,j),且值为k,所需要的最短路径,因为要找出所有路径中的最优解,所以要加超级源,省略这个过程,直接把所有点入队,可以起到相同的效果。

至于方案,用字符串记录,如果更新最短路,同时更新方案,如果新路等于最短路,则比较方案,取字典序较小的。

输出的时候枚举所有点且值为dem[i]的,找到最短的,同时比较方案字典序。、

奇葩的是,我用string比用字符数组快得多,同时代码复杂度小很多,字符数组调试了两个小时才对,但是string就很容易写对了。。string也有它的优势,尤其是比较和在字符串后面加字符,应该学会取舍。


#include <cstdio>
#include <algorithm>
#include <iostream>
#include <string>
using std::cout;
using std::min;
using std::string;

struct node
{
	long x;
	long y;
	long v;
	bool o;
};
node que[2000010];
const long qmod = 2000000;
bool hash[30][30][800];
string g[30][30][800];
const long dx[] = {-1,0,1,0};
const long dy[] = {0,-1,0,1};
long dist[30][30][800];
char map[30][30];
node u,v;
long n;
long dem[60];
long maxc = 0;

long getint()
{
	long rs=0;bool sgn=1;char tmp;
	do tmp = getchar();
	while (!isdigit(tmp)&&tmp-'-');
	if (tmp == '-'){tmp=getchar();sgn=0;}
	do rs=(rs<<3)+(rs<<1)+tmp-'0';
	while (isdigit(tmp=getchar()));
	return sgn?rs:-rs;	
}

void spfa()
{
	long l = 0;
	long r = 0;
	memset(dist,0x3f,sizeof dist);

	for (long i=1;i<n+1;i++)
		for (long j=1;j<n+1;j++)
		{
			if (isdigit(map[i][j]))
			{
				r ++;
				que[r].x = i;
				que[r].y = j;
				que[r].v = (map[i][j] - '0');
				dist[i][j][map[i][j]-'0'+260] = 0;
				g[i][j][map[i][j]-'0'+260] = map[i][j];
				que[r].o = true;
			}
		}

	while (l < r)
	{
		l ++;
		if (l == qmod)
			l = 0;
		
		u = que[l];
		hash[u.x][u.y][u.v+260] = false;
		for (long i=0;i<4;i++)
		{
			v = u;
			v.x += dx[i];
			v.y += dy[i];

			if (v.x<1||v.y<1||v.x>n||v.y>n)
				continue;

			if (isdigit(map[v.x][v.y]))
				v.v = u.o?v.v+(map[v.x][v.y]-'0'):v.v-(map[v.x][v.y]-'0');
			else
				v.o = map[v.x][v.y]=='+';

			if (v.v > 500 || v.v < -250)
				continue;

			if (dist[v.x][v.y][v.v+260] >= dist[u.x][u.y][u.v+260] + 1)
			{
				if (dist[v.x][v.y][v.v+260] > dist[u.x][u.y][u.v+260] + 1)
				{
					dist[v.x][v.y][v.v+260] = dist[u.x][u.y][u.v+260] + 1;
					g[v.x][v.y][v.v+260] = g[u.x][u.y][u.v+260]+map[v.x][v.y];
				}
				else
					g[v.x][v.y][v.v+260] = min(g[v.x][v.y][v.v+260],g[u.x][u.y][u.v+260]+map[v.x][v.y]);
				if (!hash[v.x][v.y][v.v+260])
				{
					r ++;
					que[r] = v;
					hash[v.x][v.y][v.v+260] = true;
				}
			}
		}
	}
}

int main()
{
	freopen("dance.in","r",stdin);
	freopen("dance.out","w",stdout);

	long T = getint();
	n = 0;
	while (T --)
	{
		memset(hash,0,sizeof hash);
		memset(dist,0,sizeof dist);
		for (long i=0;i<30;i++)
			for (long j=0;j<30;j++)
				for (long k=0;k<800;k++)
					g[i][j][k][0] = g[i][j][k][0] = 'z';
					
		n = getint();
		long c = getint();
		for (long i=1;i<n+1;i++)
			for (long j=1;j<n+1;j++)
			{
				do map[i][j] = getchar();
				while (!isdigit(map[i][j])&&map[i][j]!='-'&&map[i][j]!='+');
			}
		maxc = 0;
		for (long i=1;i<c+1;i++)
		{
			dem[i] = getint();
			if (dem[i] > maxc)
				maxc = dem[i];
		}
		spfa();
		for (long k=1;k<c+1;k++)
		{
			string path = "";
			long mind = 0x3f3f3f3f;
			for (long i=1;i<n+1;i++)
			{
				for (long j=1;j<n+1;j++)
				{
					if (dist[i][j][dem[k]+260] < mind)
					{
						mind = dist[i][j][dem[k]+260];
						path = g[i][j][dem[k]+260];
					}
					else if (dist[i][j][dem[k]+260]==mind)
						path = min(path,g[i][j][dem[k]+260]);
				}
			}
			cout << path << std::endl;
		}
	}

	return 0;
}



评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值