游戏:最短路,拆点

把问题抽象成图论应该不难(也许都不用抽象?),但是怎么建边怎么跑就千差万别了。

首先应该注意到的一点是坐标的范围是0~500,也就是501*501个位置,所以数组/队列不要开小。

另外题目给出的莉露露没说位置不能重复,所以每个点可能不止入队一次,仍然要注意数组大小。

刚开始一直在想复杂度与n挂钩的算法,但是它挂了。

可以发现,n与x*y的差距并不打,所以如果正解复杂度可接受n的话那么拿xy作为复杂度应该没有问题。

先大概讲一下考场上的暴力(80分,因为上面说的锅,把数组开大是88分):

考虑每个莉露露的行动:去一个位置,捡起由岐,走几步,扔出去。

如果我们要对于每一个格子进行转移而不是针对每一个莉露露的话,去一个位置捡起由岐这个操作就可以变成:

找到最近的莉露露来这个位置,再让这个莉露露去走,扔。

走和扔的话就能比较简单的转移了。

那么首先的一个问题是,怎么找到离每一个点最近的莉露露有多远?

所有的二维几何题都可以用KD-tree乱搞。貌似的确可以这也是我考场上打了一半的思路。

但是这其实就是一个BFS,以每一个莉露露为源点不断扩散直至每个点都会被搜索到一次。

复杂度O(xy)。可以考虑建一个超级源点其距离值为-1,由它连向每一个莉露露,当然也可以直接搞。

然后假如我们已经找到了这个数组叫nst吧,那么怎么跑最短路?

我们假设由岐始终没着地:在被扔之前另一个莉露露已经到了落点去准备接住了。(当然不会影响答案)

那么她就始终在莉露露的手里了,所有的莉露露只有两种操作。

1.走一步,费用C,那么就想四周连边(不用真的建边直接for枚举就行)。连4条边。

2.扔。目的地只能是本行或本列上的,还是枚举,连了x+y条边。

而因为我们假设不让由岐落地,所以在到了目的地之后还要让最近的莉露露来接住她,费用就是ax+b+c*nst[tx][ty]

所以总边数是x3级别的,复杂度也是。

另外还可以优化一下,如果你是被从这一列扔过来的,那么你不会再被扔到这一列,行同理,开一个bool数组更新最小距离时记录就好。

这样的话边数能减少一些,具体多少看测试点,反正效果还可以。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 const int xx[]={1,0,0,-1},yy[]={0,1,-1,0};
 7 #define tx qx[h]+xx[i]
 8 #define ty qy[h]+yy[i]
 9 struct ps{int x,y;long long d;friend bool operator<(ps a,ps b){return a.d>b.d;}};
10 priority_queue<ps>q;
11 bool l[505][505],r[505][505];
12 int com,nst[505][505],X,Y,n,qx[500155],qy[500155],gx,gy,sx,sy;
13 long long a,b,c,dt[505][505];
14 signed main(){//freopen("ex_game5.in","r",stdin);
15     memset(nst,0x3f,sizeof nst);memset(dt,0x3f,sizeof dt);
16     scanf("%d%d%lld%lld%lld%d%d%d",&X,&Y,&a,&b,&c,&n,&sx,&sy);
17     nst[sx][sy]=0,qx[1]=sx,qy[1]=sy;
18     for(int i=2;i<=n;++i)scanf("%d%d",&gx,&gy),nst[gx][gy]=0,qx[i]=gx,qy[i]=gy;
19     for(int h=1,t=n;h<=t;++h)for(int i=0;i<=3;++i)
20         if(tx>=0&&ty>=0&&tx<=X&&ty<=Y&&nst[tx][ty]>nst[qx[h]][qy[h]]+1)
21             ++t,nst[qx[t]=tx][qy[t]=ty]=nst[qx[h]][qy[h]]+1;//,printf("%lld %lld\n",sx,sy);
22     q.push((ps){sx,sy,dt[sx][sy]=0});
23     while(!q.empty()){
24         int x=q.top().x,y=q.top().y;long long d=q.top().d;q.pop();
25         if(dt[x][y]!=d)continue;
26         if(x==gx&&y==gy){printf("%lld\n",d);return 0;}
27         #define kx x+xx[i]
28         #define ky y+yy[i]
29         for(int i=0;i<=3;++i)if(kx>=0&&kx<=X&&ky<=Y&&ky>=0&&dt[kx][ky]>d+c)
30             l[kx][ky]=r[kx][ky]=0,q.push((ps){kx,ky,dt[kx][ky]=d+c});
31         if(!l[x][y])for(int i=0;i<x;++i)if(dt[i][y]>d+a*(x-i)+b+c*nst[i][y])
32             l[i][y]=1,r[i][y]=0,q.push((ps){i,y,dt[i][y]=d+a*(x-i)+b+c*nst[i][y]});
33         if(!l[x][y])for(int i=x+1;i<=X;++i)if(dt[i][y]>d+a*(i-x)+b+c*nst[i][y])
34             l[i][y]=1,r[i][y]=0,q.push((ps){i,y,dt[i][y]=d+a*(i-x)+b+c*nst[i][y]});
35         if(!r[x][y])for(int i=0;i<y;++i)if(dt[x][i]>d+a*(y-i)+b+c*nst[x][i])
36             r[x][i]=1,l[x][i]=0,q.push((ps){x,i,dt[x][i]=d+a*(y-i)+b+c*nst[x][i]});
37         if(!r[x][y])for(int i=y+1;i<=Y;++i)if(dt[x][i]>d+a*(i-y)+b+c*nst[x][i])
38             r[x][i]=1,l[x][i]=0,q.push((ps){x,i,dt[x][i]=d+a*(i-y)+b+c*nst[x][i]});
39     }
40 }

100%算法就是稍微优化了一下建边。

稍微改变状态定义,其实每次被扔出去可以被分为两个阶段,从那个ax+b能yy一下。

首先花费b的费用起飞,然后沿着一个方向飞,每飞一个格子的费用是a,着陆的费用就是最近的莉露露捡起她的费用c×nst。

当然也可以不飞,一步一步走,这里和暴力是一样的。

那么因为被扔的操作每次也只会向上下左右四个格子转移了,所以复杂度有了保证。

然而你现在需要弄清楚她有没有起飞有没有着陆,如果在飞的话她是在横着飞还是竖着。

拆点,拆成3个,分别表示0在地上,1在横着飞,2在竖着飞。

可以从0转移到1和2,费用为b。

可以从1,2转移到0,费用为c×nst。

可以从0转移到相邻4个格子的0,费用为c。

可以从1转移到左右两个格子的1,费用为a。

可以从2转移到上下两个格子的2,费用为a。

总边数10n2。跑Dijkstra,复杂度乘个log,问题不大。

据secret测试可以暴力把所有边都建出来也能A,但是不推荐大常数者使用。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<queue>
 5 #include<ctime>
 6 using namespace std;
 7 const int xx[]={1,-1,0,0},yy[]={0,0,-1,1};
 8 #define tx qx[h]+xx[i]
 9 #define ty qy[h]+yy[i]
10 struct ps{int x,y,st;long long d;friend bool operator<(ps a,ps b){return a.d>b.d;}};
11 priority_queue<ps>q;
12 int com,X,Y,n,qx[500155],qy[500155],gx,gy,sx,sy;
13 long long a,b,c,dt[3][505][505],nst[505][505];
14 signed main(){//freopen("ex_game5.in","r",stdin);
15     memset(nst,0x3f,sizeof nst);memset(dt,0x3f,sizeof dt);
16     scanf("%d%d%lld%lld%lld%d%d%d",&X,&Y,&a,&b,&c,&n,&sx,&sy);
17     nst[sx][sy]=0,qx[1]=sx,qy[1]=sy;
18     for(int i=2;i<=n;++i)scanf("%d%d",&gx,&gy),nst[gx][gy]=0,qx[i]=gx,qy[i]=gy;
19     for(int h=1,t=n;h<=t;++h)for(int i=0;i<=3;++i)
20         if(tx>=0&&ty>=0&&tx<=X&&ty<=Y&&nst[tx][ty]>nst[qx[h]][qy[h]]+c)
21             nst[qx[++t]=tx][qy[t]=ty]=nst[qx[h]][qy[h]]+c;
22     q.push((ps){sx,sy,0,dt[0][sx][sy]=0});
23     while(!q.empty()){
24         int x=q.top().x,y=q.top().y,st=q.top().st;long long d=q.top().d;q.pop();
25         if(dt[st][x][y]!=d)continue;
26         if(x==gx&&y==gy){printf("%lld\n",d);return 0;}
27         #define kx x+xx[i]
28         #define ky y+yy[i]
29         if(!st){
30             for(int i=0;i<=3;++i)if(kx>=0&&kx<=X&&ky<=Y&&ky>=0&&dt[0][kx][ky]>d+c)
31                 q.push((ps){kx,ky,0,dt[0][kx][ky]=d+c});
32             for(int s=1;s<=2;++s)if(dt[s][x][y]>d+b)
33                 q.push((ps){x,y,s,dt[s][x][y]=d+b});
34         }else{
35             for(int i=st*2-2;i<=st*2-1;++i)if(kx>=0&&kx<=X&&ky<=Y&&ky>=0&&dt[st][kx][ky]>d+a)
36                 q.push((ps){kx,ky,st,dt[st][kx][ky]=d+a});
37             if(dt[0][x][y]>d+nst[x][y])q.push((ps){x,y,0,dt[0][x][y]=d+nst[x][y]});
38         }
39     }
40 }

代码复杂度不大,1.5k

原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11461203.html

时间: 2024-11-08 14:30:34

游戏:最短路,拆点的相关文章

bzoj2464: 中山市选[2009]小明的游戏(最短路)

2464: 中山市选[2009]小明的游戏 题目:传送门 题解: 最短路的裸题... 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int dx[5]={0,1,-1,0,0}; 8 int dy[5]={0,0,0,-1,1}; 9 st

【BZOJ2464】【中山市选2009】小明的游戏 最短路水过

题解:最短路pqspfa200ms,一眼题, 另一种想出来没写的做法:二分答案,上界n+m 时间复杂度O(n*m*log(n+m)),二分+深搜看能不能找到t 最短路代码: #include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define N 505 #define NN 251000 #define inf 0x3f3f3f3f using namespac

BZOJ 2464 中山市选 2009 小明的游戏 最短路

题目大意:给出一个地图,如果经过两个不同的区块,需要花费1,否则不需要花费.问从st到ed最小需要花费多少. 思路:签到题. #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 510 #define MAXP 250010 #define MAXE 2000010 using name

图论常用算法之一 POJ图论题集【转载】

POJ图论分类[转] 一个很不错的图论分类,非常感谢原版的作者!!!在这里分享给大家,爱好图论的ACMer不寂寞了... (很抱歉没有找到此题集整理的原创作者,感谢知情的朋友给个原创链接) POJ:http://poj.org/ 1062* 昂贵的聘礼 枚举等级限制+dijkstra 1087* A Plug for UNIX 2分匹配 1094 Sorting It All Out floyd 或 拓扑 1112* Team Them Up! 2分图染色+DP 1125 Stockbroker

大神刷题表

9月27日 后缀数组:[wikioi3160]最长公共子串 dp:NOIP2001统计单词个数 后缀自动机:[spoj1812]Longest Common Substring II [wikioi3160]最长公共子串 [spoj7258]Lexicographical Substring Search 扫描线+set:[poj2932]Coneology 扫描线+set+树上删边游戏:[FJOI2013]圆形游戏 结论:[bzoj3706][FJ2014集训]反色刷 最小环:[poj1734

搜狐畅游CEO王滔辞职

凤凰科技讯 11月3日消息,搜狐发布公告确认搜狐畅游CEO离职,公告称王滔因个人原因辞去畅游首席执行官职务,将继续担任畅游公司董事和首席产品官. 据搜狐公告,董事会任命搜狐总裁余楚媛与畅游总裁陈德文为畅游公司联席CEO.张朝阳表示,表示:"出于对王滔先生个人意愿的尊重,董事会接受他的辞职请求." 据了解,王滔昨晚在公司微信群表示已离职,称"祝福大家,好好工作,我继续使用你们的产品",王滔未来将会选择创业. 2004年王滔加入搜狐,担任北京火狐数码科技有限公司总经理,

【例题收藏】◇例题&#183;I◇ Snuke&#39;s Subway Trip

◇例题·I◇ Snuke's Subway Trip 题目来源:Atcoder Regular 061 E题(beta版) +传送门+ 一.解析 (1)最短路实现 由于在同一家公司的铁路上移动是不花费的,只有在换乘时会花费1日元.我们可以视换乘的花费为点权--当换乘到不同公司时花费1,同一家公司时花费0. 对于这种点权因情况而变化的题,最常见的做法便是拆点.设节点 u 相连的铁路由 c1,c2,...,cp p个公司修建,则将节点u拆分为节点 uc1~ucp,点uci表示到达点u的铁路是由公司c

hdu4725 拆点+最短路

题意:有 n 个点,每个点有它所在的层数,最多有 n 层,相邻两层之间的点可以互相到达,消耗 c (但同一层并不能直接到达),然后还有一些额外的路径,可以在两点间互相到达,并且消耗一定费用.问 1 点到 n 点的最小花费 将每一层拆成两个点,分别为进入层和出发层,然后相邻层的出发层可以指向进入层,花费 c,每个点可以到达其出发层,而进入层可以到达该点,花费 0 ,最后建立其余双向边,最短路 1 #include<stdio.h> 2 #include<string.h> 3 #in

求次短路 codevs 1269 匈牙利游戏

codevs 1269 匈牙利游戏 2012年CCC加拿大高中生信息学奥赛 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Welcome to the Hungary Games! The streets of Budapest form a twisted network of one-way streets. 欢迎来到匈牙利游戏!布达佩斯(匈牙利首都)的街道形成了一个弯曲的单向网络. You have been fo