【xdoj难题集】1202 The Offer - Lunatic(附优化版)

 

说实话2017年的校赛挺难的,最后三道都不简单,这道题一开始吓了我一大跳,想着如果用最短路算法,魔法阵怎么也要上千万条路径,不过之后看到海拔最高只有100之后总算是安心了。

 

 

先说一说大体思路,首先肯定想的是用最短路算法,每个点初始只有最多四条路,每条路的长度也很好求。重点是这个魔法阵,肯定要从高度入手,初始的想法肯定是构造一条轨道,轨道上有高度范围个节点,每个处于法阵里的点和所有满足高度范围的轨道点相连,但这样遇到一个小问题,就是这样会造成一个重合的问题,可能两个本来不该连接的点却因为范围里有一个公共点而相连了,说白了就是这样高度差的绝对值会加倍,但是因为不知道高度差的奇偶所以还不好用一半,解决方法是构造两条轨道,出轨和入轨,我们只能进入或离开从法阵点高度的轨道,但是两条轨道之间建立联系,使得入轨和出轨可以准确的表示从入轨高度到出轨高度,这样是否连接就只跟高度差有关了。

 

 

 

具体实施倒是不难,有一个细节就是坐标的映射,这是个细节问题首先为了压缩我把坐标变成了0开头的,这样屏幕上的点就用0到9999表示,之后1010个点表示入轨,再之后1010表示出轨(10 * 101个点),所以k也从0开头,没过101是另一个k。

 

1输入输出的同时构图,记坐标的同时就能连线,然后每个法阵按照刚才的思路连线。

 

 

2用dijkstra算法计算最短路

 

 

最后说说优化,其实细节还挺多的,首先用了优先队列,除了这个常见的,可以注意到t的值很大,但是其实就算不走法阵最大的路程也就在40000以内,所以t大于40000的就可以直接跳过了,另外在构造轨道的时候有可能很多高度没有涉猎的点,这个时候就不需要构造和他们有关的轨道了。

 

 

 

贴代码

 

# include <stdio.h>
# include <algorithm>
# include <queue>
# include <vector>

using namespace std;

typedef pair<int , int> P;

const int MAX_N = 100;
const int MAX_E = 12500;
const int INF = 10000000;

int N, M, R, T;
int h[MAX_N][MAX_N];
int d[MAX_E];
int sx, sy, ox, oy;

vector<P> G[MAX_E]; 

void sea()
{
	priority_queue<P , vector<P> , greater<P> > que;
	
	d[sx * 100 + sy] = 0;
	que.push(P(0 , sx * 100 + sy));
	
	while(!que.empty())
	{
		P p = que.top();
		que.pop();
		
		int v = p.second;
		if(d[v] < p.first)
			continue;
		
		int i;
		for(i = 0 ; i < G[v].size() ; i++)
		{
			P e = G[v][i];
			if(d[e.first] > d[v] + e.second)
			{
				d[e.first] = d[v] + e.second; 
				que.push(P(d[e.first] , e.first)); 
			}
		}
	}
}

int main()
{
	scanf("%d", &T);
	
	while(T--)
	{
		scanf("%d %d %d", &N, &M, &R);
		
		int i, j, k;
		for(i = 0 ; i < N ; i++)
		{
			for(j = 0 ; j < M ; j++)
			{
				scanf("%d", &h[i][j]);
				
				if(i)
				{
					G[100 * i + j].push_back(P(100 * i + j - 100 , h[i][j] + h[i - 1][j]));
					G[100 * i + j - 100].push_back(P(100 * i + j , h[i][j] + h[i - 1][j]));
				}
				if(j)
				{
					G[100 * i + j].push_back(P(100 * i + j - 1 , h[i][j] + h[i][j - 1]));
					G[100 * i + j - 1].push_back(P(100 * i + j , h[i][j] + h[i][j - 1]));
				}
			}
		}
		
		int ax, ay, bx, by, t, p;
		bool used[101];
		for(i = 0 ; i < R ; i++)
		{
			scanf("%d %d %d %d %d %d", &ax, &ay, &bx, &by, &t, &p);
			
			if(t > 40000)
				continue;
			
			ax--;
			ay--;
			bx--;
			by--;
			fill(used , used + 101 , 0);
			for(j = ax ; j <= bx ; j++)
			{
				for(k = ay ; k <= by ; k++)
				{
					G[100 * j + k].push_back(P(10000 + 101 * i + h[j][k] , 0));
					G[11010 + 101 * i + h[j][k]].push_back(P(100 * j + k , 0));
					
					used[h[j][k]] = 1;
				}
			}
			
			for(j = 0 ; j <= 100 ; j++)
			{
				if(!used[j])
					continue;
				for(k = max(0 , j - p) ; k <= min(100 , j + p) ; k++)
				{
					if(!used[k])
						continue;
						
					G[10000 + 101 * i + j].push_back(P(11010 + 101 * i + k , t));
				}
			}
		}
		
		scanf("%d %d %d %d", &sx, &sy, &ox, &oy);
		
		sx--;
		sy--;
		ox--;
		oy--;
		
		fill(d , d + MAX_E , INF);
		sea();
		printf("%d\n", d[ox * 100 + oy]);
		
		for(i = 0 ; i < N ; i++)
		{
			for(j = 0 ; j < M ; j++)
				G[100 * i + j].clear();
		}
		
		for(i = 10000 ; i < 12500 ; i++)
			G[i].clear();
	}
	
	return 0;
}

 

2018/8/15

今天闲得无聊又写了一发,加入了一个输入挂,最短路用的是spfa(主要好久没用了熟练一下),感觉提升还是不少的,仅仅从时间来说就快了6倍左右,果然现在对于时间的优化越来越好了,有时候回头看看之前做过的问题,优化之,也算是一个不错的休闲了。

贴代码

# include <cstdio>
# include <cstring>
# include <queue>

using namespace std;

namespace io {
    const int L=(1<<21)+1;
    char ibuf[L],*iS,*iT,obuf[L],*oS=obuf,*oT=obuf+L-1,c,st[55];int f,tp;
	#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,L,stdin),(iS==iT?EOF:*iS++)):*iS++)
    inline void flush() {
        fwrite(obuf,1,oS-obuf,stdout);
        oS=obuf;
    }
    inline void putc(char x) { *oS++=x; if (oS==oT) flush(); }
    template<class I> inline void gi(I&x) {
        for (f=1,c=gc();c<'0'||c>'9';c=gc()) if (c=='-') f=-1;
        for (x=0;c<='9'&&c>='0';c=gc()) x=x*10+(c&15); x*=f;
    }
    template<class I> inline void print(I x) {
        if (!x) putc('0');
        if (x<0) putc('-'),x=-x;
        while (x) st[++tp]=x%10+'0',x/=10;
        while (tp) putc(st[tp--]);
    }
    inline void gs(char*s, int&l) {
        for (c=gc();c<'a'||c>'z';c=gc());
        for (l=0;c<='z'&&c>='a';c=gc()) s[l++]=c;
        s[l]=0;
    }
    inline void ps(const char*s) { for (int i=0,n=strlen(s);i<n;i++) putc(s[i]); }
    struct IOFLUSHER{ ~IOFLUSHER() { flush(); } } _ioflusher_;
};
using io::putc; 
using io::gi;
using io::gs;
using io::ps;
using io::print;

# define pos(i , j) (i) * M + j

struct node
{
	int to, cos, next;
};

const int MAX_N = 1e5 + 10000;
const int MAX_M = 100;

int head[MAX_N];
node edge[10 * MAX_N];
int edg;

bool used[MAX_N];
int d[MAX_N];

int H[MAX_M][MAX_M];
int N, M, R;
int T;

int sx, sy, gx, gy;

void ad(int from , int to , int cost , bool f)
{
	edge[edg].to = to;
	edge[edg].cos = cost;
	edge[edg].next = head[from];
	head[from] = edg++;
	
	if(!f)
		return;
		
	edge[edg].to = from;
	edge[edg].cos = cost;
	edge[edg].next = head[to];
	head[to] = edg++;
}

int spfa(int s , int t)
{
	memset(used , 0 , sizeof(used));
	memset(d , 0x3f , sizeof(d));
	
	queue<int> que;
	que.push(s); 
	d[s] = 0;
	
	while(!que.empty())
	{
		int x = que.front();
		que.pop();
		
		used[x] = 0;
		
		int i;
		for(i = head[x] ; i ; i = edge[i].next)
		{
			int to = edge[i].to, cos = edge[i].cos;
			
			if(d[to] > d[x] + cos)
			{
				d[to] = d[x] + cos;
				if(!used[to])
				{
					used[to] = 1;
					que.push(to);
				}
			}
		}
	}
	
	return d[t];
}

int main()
{
	gi(T);
	
	while(T--)
	{
		edg = 1;
		memset(head , 0 , sizeof(head));
		
		gi(N);
		gi(M);
		gi(R);
		
		int i, j, k;
		for(i = 0 ; i < N ; i++)
		{
			for(j = 0 ; j < M ; j++)
			{
				gi(H[i][j]);
				
				if(i)
					ad(pos(i - 1 , j) , pos(i , j) , H[i][j] + H[i - 1][j] , 1);
				if(j)
					ad(pos(i , j - 1) , pos(i , j) , H[i][j] + H[i][j - 1] , 1);
			}
		}
		
		int now = N * M; 
		for(i = 0 ; i < R ; i++)
		{
			int x1, y1, x2, y2, t, p;
			
			gi(x1);gi(y1);gi(x2);gi(y2);gi(t);gi(p);
			x1--;y1--;x2--;y2--;
			
			if(t > 40000)
				continue;
			
			for(j = x1 ; j <= x2 ; j++)
			{
				for(k = y1 ; k <= y2 ; k++)
				{
					ad(pos(j , k) , now + H[j][k] , 0 , 0);
					ad(now + 101 + H[j][k] , pos(j , k) , 0 , 0);
					used[H[j][k]] = 1;
				}
			}
			
			memset(used , 0 , sizeof(bool) * 101);
			for(j = 0 ; j <= 100 ; j++)
			{
				if(!used[j])
					continue;
					
				int siz = min(100 , j + p);
				for(k = max(0 , j - p) ; k <= siz ; k++)
				{
					if(!used[k])
						continue;
					ad(now + j , now + 101 + k , t , 0);
				}
			}
			
			now += 202; 
		}
		gi(sx);gi(sy);gi(gx);gi(gy);
		sx--;sy--;gx--;gy--;
		
		printf("%d\n", spfa(pos(sx , sy) , pos(gx , gy)));
	}
	
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值