说实话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;
}