NOIP2017tg【逛公园】 题解
先说点别的
emmm……,这是本蒟蒻的第一篇题解,大佬们勿喷QwQ(要不是看到写题解可以加贡献,我才……)
可以看到标签,是2017年提高的题目,好像是Day1T3,感觉提高考这样的题目挺好的,至少考场上也可以很快想到做法。唉……,我太菜了,调这道题调了一下午才满,要是在考场上肯定死。
看了题解几位奆佬的思路,有拓扑的,记忆化搜索的,跑反图的,感觉和自己想得不是很一样,于是想讲一下本蒟蒻写这道题的思路。
几个好消息
1)此题不卡SPFA!!!请SPFA党放心使用!!!
2)此题部份分给得很足,直接使用P1608路径统计的方法可以过30分。
关于此题
前置技能:
2、dp
题意:应该很清楚了吧,求1至n的所有路径中,小于等于dis[n]+K的路径数,结果取膜P。注意,图有可能会出现0边和0环。
既然我们选择做这道题,就要奔着满分前进!
做法
本蒟蒻选择的做法是SPFA加dp,看很多大佬都要加拓扑什么的来确定dp更新顺序和判0环,不过本蒟蒻表示完全没有必要,其实在跑SPFA的过程中,我们就可以随便把这些问题解决掉。
1、判0环
判0环和判负环其实差不多,只需要在SPFA中加一个判断即可,但是,由于此题卡时间卡得很紧,所以我们机房某大佬教了我卡带,这样判0环听说可以快很多。
if(dis[v]>=dis[u]+cost[i])
注意:SPFA这里需要把'>'改成">=",不然判不了0环
剩下的判0环方法就和判负环差不多,如果还不明白可以看代码。(另外提供一种判0环方式,把0边全部抽出来,然后判环,拓扑什么的)
2、确定dp更新顺序
这个其实也很简单,我们不难发现,其实只有0边两个点的顺序需要确定更新顺序,其他只需要按dis从小到大的顺序更新就可以了。于是我们在SPFA中加一个更新就可以了。
if(id[v]<id[u]+1)id[v]=id[u]+1;
这样即可确定dp更新顺序。dp前先按dis为第一关键字,id为第二关键字排序即可。
3、dp
额……,讲了怎么久的dp更新顺序,可能你们还不懂怎么dp吧,我的dp方法和Kelin大佬的一样,都是f[i][j]表示1至i的路径中,小于等于dis[i]+j的路径数。
转移也很简单,只要(dis[u]+j+(u至v的长度)-dis[v]<=K)
那么就将f[v][dis[u]+j+(u至v的长度)-dis[v]]+=f[u][j];
最后记得取膜蛤!dp的更新顺序记得按关键字排序啊。
还有,本蒟蒻由于dp学得不是很好,所以讲得如果不懂的话,可以看代码或者Kelin大佬讲的,我觉得他dp讲得比我好啊QAQ。
for(int k=0;k<=kk;k++) //dp
{
for(int j=1;j<=n;j++)
{
int u=a[j].pos,d=dis[u];
if(d>=inf)continue; //小剪枝
for(int i=head[u];i!=-1;i=Next[i])
{
int v=to[i];
if(cost[i]-dis[v]+d+k<=kk) //如果当前到v的状态的花费小于kk就更新
{
f[v][cost[i]-dis[v]+d+k]+=f[u][k]; //更新
f[v][cost[i]-dis[v]+d+k]%=mod; //记得取mod
}
}
}
}
dp代码如上,还是比较好想到的呢。
好了那么思路也就差不多了,总体的难度其实不是很大,不过这样写的时间复杂度会比用拓扑的高,毕竟在找0环的时候比较耗时间,但是方法肯定是没问题的,接下来来看全部代码吧。
AC代码:
#include<bits/stdc++.h>
#define maxm 800000
#define maxn 400000
#define inf 20010100
using namespace std;
int cnt,from[maxm],to[maxm],cost[maxm],Next[maxm],head[maxm],cont[maxm],cb[maxn],id[maxn];//嗯,定义有点丑蛤
int dis[maxn],vis[maxn],f[maxn][65],ans;
int n,m,x,y,z,mod,kk;
struct kkk{
int dis,id,pos;
}a[maxn];
queue<int>q;
int cmp(kkk a,kkk b){ //排序操作
if(a.dis==b.dis)return a.id<b.id; //记得以dis为第一关键字哦~~~,id为第二关键字
else return a.dis<b.dis;
}
bool SPFA(int S){ //SPFA求最短路
while(!q.empty())q.pop();
for(int i=1;i<=n;i++)dis[i]=inf,vis[i]=0,id[i]=0;
vis[S]=1;q.push(S);dis[S]=0;
int sum=0; //上面为初始化
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
sum+=cb[u]+1;if(sum>2000000)return false; //卡带判0环
for(int i=head[u];i!=-1;i=Next[i])
{
int v=to[i];
if(dis[v]>=dis[u]+cost[i])
{
if(++cont[v]>=n)return false; //当然普通判0环也少不了
dis[v]=dis[u]+cost[i];
if(id[v]<id[u]+1)id[v]=id[u]+1; //id确定0边两个端点的更新顺序
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
return true;
}
void add(int x,int y,int z){ //建边
cnt++;
cost[cnt]=z;cb[x]++;
from[cnt]=x;to[cnt]=y;
Next[cnt]=head[x];head[x]=cnt;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(head,-1,sizeof(head)); //初始化{
memset(cont,0,sizeof(cont));ans=0;
memset(a,0,sizeof(a));cnt=0; //}初始化
scanf("%d%d%d%d",&n,&m,&kk,&mod); //输入{
for(int i=1;i<=m;i++)
scanf("%d%d%d",&x,&y,&z),add(x,y,z);//}输入
bool flag=SPFA(1); //SPFA跑最短路
if(flag==false) //随便判0环
{printf("-1\n");continue;}
//***********************以上为基本操作-分界线-以下为dp求解***********************
for(int i=1;i<=n;i++)a[i].pos=i,a[i].dis=dis[i],a[i].id=id[i]; //dp的初始化
sort(a+1,a+n+1,cmp); //进行排序
//f[i][j]表示1至i的路径中,小于等于dis[i]+j的路径数
memset(f,0,sizeof(f));f[1][0]=1;//dp初始化
for(int k=0;k<=kk;k++) //dp
{
for(int j=1;j<=n;j++)
{
int u=a[j].pos,d=dis[u];
if(d>=inf)continue; //小剪枝
for(int i=head[u];i!=-1;i=Next[i])
{
int v=to[i];
if(cost[i]-dis[v]+d+k<=kk) //如果当前到v的状态的花费小于kk就更新
{
f[v][cost[i]-dis[v]+d+k]+=f[u][k]; //更新
f[v][cost[i]-dis[v]+d+k]%=mod; //记得取mod
}
}
}
}
for(int i=0;i<=kk;i++)
ans+=f[n][i],ans%=mod; //最后统计答案
printf("%d\n",ans);
}
}
NOIP2017tg【逛公园】 题解的更多相关文章
- NOIP2017 逛公园 题解报告 【最短路 + 拓扑序 + dp】
题目描述 策策同学特别喜欢逛公园.公园可以看成一张NNN个点MMM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,NNN号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花 ...
- [NOIP2017]逛公园 题解
我连D1T3都不会我联赛完蛋了 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负 ...
- 【题解】洛谷P3953 [NOIP2017TG] 逛公园(记忆化搜索+SPFA)
题目来源:洛谷P3953 思路 先用SPFA求一遍最短路 在求最短路的同时可以把所有点到终点的最短路求出来 dis数组 注意要反向SPFA 因为从起点开始可能会走到一些奇怪的路上导致时间负责度增加 ...
- NOIP 2017 逛公园 题解
题面 这道题是一道不错的计数类DP: 首先我们一定要跑一遍dijkstra来求得每个点到1号点的最短路: 注意题干,题中并没有说所有点都可以到达n好点,只说了存在一条1号点到n号点的路径:所以我们在反 ...
- 线段树 || BZOJ1756: Vijos1083 小白逛公园 || P4513 小白逛公园
题面:小白逛公园 题解: 对于线段树的每个节点除了普通线段树该维护的东西以外,额外维护lsum(与左端点相连的最大连续区间和).rsum(同理)和sum……就行了 代码: #include<cs ...
- 【题解】NOIP2017逛公园(DP)
[题解]NOIP2017逛公园(DP) 第一次交挂了27分...我是不是必将惨败了... 考虑这样一种做法,设\(d_i\)表示从该节点到n节点的最短路径,\(dp(i,k)\)表示从\(i\)节点 ...
- [vijos P1083] 小白逛公园
不知怎地竟有种错觉此题最近做过= =目测是类似的?那道题貌似是纯动归? 本来今晚想做两道题的,一道是本题,一道是P1653疯狂的方格取数或NOI08 Employee,看看现在的时间目测这个目标又达不 ...
- Bzoj 1756: Vijos1083 小白逛公园 线段树
1756: Vijos1083 小白逛公园 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1021 Solved: 326[Submit][Statu ...
- BZOJ 1756: Vijos1083 小白逛公园
题目 1756: Vijos1083 小白逛公园 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 856 Solved: 264[Submit][Sta ...
- [NOIp 2017]逛公园
Description 策策同学特别喜欢逛公园.公园可以看成一张$N$个点$M$条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,$N$号点是公园的出口,每条边有一个非负权值, 代表策策经 ...
随机推荐
- 【Hibernate 多对多】
HibernateManytoMany public class HibernateManytoMany { //演示维护第三张表 @Test public void testTable2() { S ...
- centos 安装phpize
yum -y install php-devel 然后 /usr/bin/phpize
- Python 爬取 热词并进行分类数据分析-[云图制作+数据导入]
日期:2020.01.28 博客期:136 星期二 [本博客的代码如若要使用,请在下方评论区留言,之后再用(就是跟我说一声)] 所有相关跳转: a.[简单准备] b.[云图制作+数据导入](本期博客) ...
- 笔记-爬虫部署及运行工具-scrapydweb
笔记-爬虫部署及运行工具-scrapydweb 1. 简介 scrapyd是爬虫部署工具,但它的ui比较简单,使用不是很方便. scrapydweb以scrapyd为基础,增加了ui界面和监 ...
- Maven 项目中使用 logback
添加依赖 <dependency> <groupId>net.logstash.logback</groupId> <artifactId>logsta ...
- Python学习第十七课——组合
组合1 #组合 1 class Hand: pass class Foot: pass class Trunk: pass class Head: pass class Person: def __i ...
- Python的常用库
读者您好.今天我将介绍20个属于我常用工具的Python库,我相信你看完之后也会觉得离不开它们.他们是: Requests.Kenneth Reitz写的最富盛名的http库.每个Python程序员都 ...
- 四、linux基础-系统目录_安装_相关命令_调度
4 系统目录-安装-版本4.1系统目录Linux的文件系统是采用级层式的树状目录结构,在此结构中的最上层是根目录“/”,然后在此目录下再创建其他的目录. 在装完Linux系统以后会自动生成一下等目录, ...
- QT5静态编译工程(arm交叉编译)
1.首先,QT编译环境默认是动态库,要编译静态程序是不可能的,所以要下载QT源码,重新编译QT编译环境 2.下载QT源码(5.13版本):http://download.qt.io/developme ...
- IDEA中找不到spring的配置文件,或者不存在某个目录(比如没有src 目录)
比如 项目中src目录找不到了,解决方式为: 这类问题都是设置这儿.