题目描述

小明所在的世界上一共有n个城市,城市间有m条双向道路。小明现在在城市1,他想到位于城市n的小韩隆家询问他为什么没有将自己的五三复原完成。由于小韩隆手下有许多小弟,小明担心自己可能再也回不来,所以他要先到达k个城市(编号为2到k+1)叫上自己的小弟,再到小韩隆家 。一 个城市有且只有一个小弟。但是有的小弟也曾被小明嘲讽过,不愿为小明卖命,所以小明需要先叫上其他的某些小弟才能叫到这个小弟。小明想要尽早的叫上所有k个小弟向小韩隆复仇,但是智力所限,他无法算出他找完k个小弟之后到小韩隆家的最少时间。于是他找到了你,想让你帮他算算。

输入格式

第一行为三个整数n、m、k,分别表示城市的数量、双向道路的数量和小弟的数量。接下来的m行每行三个整数u、V、I,表示一条路的两个端点和通过所需的时间。第m+2行为一个整数q,表示小弟有q个依赖关系。接下来q行每行两个整数x、y,表示要叫位于城市y的小弟必须先叫位于城市x的小弟。注意:由于小明十分受人厌恶,一个小弟可能依赖于不止一个小弟。经过一个城市不一定要立刻叫上该城市的小弟。

输出格式

一个整数,表示小明叫上所有k个小弟后到达小韩隆家的最短时间。

输入样例

5 7 3
1 2 3
2 3 4
3 1 2
1 4 3
4 5 5
2 5 3
5 3 2
1
2 4

输出样例

16

其他说明

【样例解释】小明行走的路线1->2->1->4->1->3->5。

对于20%的数据:n<=10,k<=2。

对于40%的数据:n<=1000,k<=10。

对于另外30%的数据,小明觉得小韩隆太鶸,不屑叫小弟。

对于100%的数据:1<= n<=20000,1<=m<=200000,0<=k<=20,0<=q<=100000,保证没有重边和自环、城市间两两连通、答案在 int 范围中。

思路

n、m很大,都跑一遍最短路会炸。参考到k很小,我们可以想到:其实最多也只是跑21个单源最短路,因为剩下的点和这道题没有直接关系。



首先我们要确定的是,必须从起点(1)出发,带上每个小弟(k个),到达终点(n),才能算作成功。至少需要经过k+2个点。

那么,我们要做的就是安排这个顺序,起点和终点不能动,只能安排中间k个小弟的顺序。

在保证都能叫到小弟的情况下,可能会有多种走的方式,所以不能用拓扑排序,例如题解有:

  • 1-2-3-4-5...ans=17
  • 1-2-4-3-5...ans=16
  • 1-3-2-4-5...ans=17

所以,只要全排列中间几个小弟,检测是否合理,两两查找相邻最短路即可,我怕数据不过,用记忆化记一下。

大致流程

  1. 数据比较大,只能用邻接表存图;存小弟的关系用vector来存,省时省力。
  2. 接下来列出1、2...k、k+1、n的全排列(一共k+2个数,不要搞错,懒,直接用next_permutation)。
  3. 对每一种排列判断,一开始觉得是不是要像并查集那样查(就是把它所有长辈都查一遍,是不是在他前面),后来发现只用查父亲就可以了,因为有父亲的时候一定也查过了父亲的父亲,以此类推。从头查,到了一个数标记,如果他父亲没出现在前面就return。
  4. 找所有两两相邻的(全排列中)点的最短时间,他们的和与ans比,取小的即可。
  5. 输出ans即为答案。

参考代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
const int inf = 0x3f3f3f3f,maxm=400002,maxn=20001;
struct Edge{
int next,to,dis;//标准建边结构体
}e[maxm];
vector<int> f[maxn];//接i小弟的时候必须有f[i]小弟
//book和vis是表示访问状态、cnt全排列用、 dis[i][j]表示从i~j的最短时间
int n,m,k,q,x,y,u,s,v,w,i,j,r,minn,numE=0,ans=inf;
int book[maxn],vis[maxn],head[maxm],dis[25][maxn],cnt[21];
struct cmp{
//优先队列出队顺序从小打大
bool operator()(int x,int y){
return dis[s][x]>dis[s][y];
}
};
//建边函数,将同一个出发点的边给集合起来。
void addEdge(int from,int to,int dis){
e[++numE].next=head[from];
e[numE].to=to;
e[numE].dis=dis;
head[from]=numE;
}
bool check(){//能否接到全部k个小弟
memset(book,0,sizeof(book));//清空数组
for(i=1;i<k+1;i++){//起点终点不用查
book[cnt[i]]=1;//先标记
for(j=0;j<f[cnt[i]].size();j++)
//如果他的大哥没来,他也不能走
if(!book[f[cnt[i]][j]])return false;
}
return true;
}
void dijkstra(int x){//dijkstra+优先队列优化
s=x;
memset(vis,0,sizeof(vis));//清空数组
priority_queue<int,vector<int>,cmp> q;
dis[s][s]=0;q.push(s);
while(!q.empty()){
int u=q.top();q.pop();//找出时间最小的来松弛
if(vis[u])continue;
vis[u]=1;
for(j=head[u];j;j=e[j].next){//和他相邻的边s
int v=e[j].to;
dis[s][v]=min(dis[s][v],dis[s][u]+e[j].dis);//松弛
q.push(v);//继续松弛
}
}
}
void find(int u,int v,int l){
//最后一项不能交换
if(l!=k&&dis[v][u]!=inf)dis[u][v]=dis[v][u];
else dijkstra(u);
}
int main(){
memset(dis,0x3f3f3f3f,sizeof(dis));
cin >> n >> m >> k;
for(i=1;i<=n;i++)vis[i]=0;
for(i=1;i<=k+1;i++)cnt[i-1]=i;//全排列
cnt[k+1]=n;//全排列用
for(i=0;i<m;i++){
cin >> u >> v >> w;
addEdge(u,v,w);addEdge(v,u,w);//建边
}
cin >> q;
for(i=0;i<q;i++){
cin >> x >> y;
f[y].push_back(x);//要注意可以多个大哥
}
do{
if(!check())continue;//如果顺序错的就跳过
int p=0;
for(i=0;i<k+1;i++){//找每两个相邻最短时间;
if(dis[cnt[i]][cnt[i+1]]==inf)
find(cnt[i],cnt[i+1],i);
p+=dis[cnt[i]][cnt[i+1]]; }
ans=min(ans,p);//是不是最短时间?
}while(next_permutation(cnt+1,cnt+k+1)); //全排列
cout << ans;
return 0;
}

TMOOC-1709-小明复仇的更多相关文章

  1. 小明的密码-初级DP解法

    #include #include #include using namespace std; int visited[5][20][9009];// 访问情况 int dp[5][20][9009] ...

  2. 小明系列问题――小明序列(LIS)

    小明系列问题――小明序列 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit ...

  3. ACM 擅长排列的小明

    擅长排列的小明 时间限制:1000 ms  |  内存限制:65535 KB 难度:4   描述 小明十分聪明,而且十分擅长排列计算.比如给小明一个数字5,他能立刻给出1-5按字典序的全排列,如果你想 ...

  4. ACM 懒省事的小明

    懒省事的小明 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述       小明很想吃果子,正好果园果子熟了.在果园里,小明已经将所有的果子打了下来,而且按果子的不同种 ...

  5. 管闲事的小明-nyoj51

    描述某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……,L,都种有一棵 ...

  6. HDU2096 小明A+B

    入门级都没到的水题!看到顺便就做了,AC记录喜+1 Description 小明今年3岁了, 现在他已经能够认识100以内的非负整数, 并且能够进行100以内的非负整数的加法计算. 对于大于等于100 ...

  7. 小明A+B[HDU2096]

    小明A+B Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  8. nyist 676 小明的求助

    http://acm.nyist.net/JudgeOnline/problem.php?pid=676 小明的求助 时间限制:2000 ms  |  内存限制:65535 KB 难度:2   描述 ...

  9. nyist 604 小明的难题

    http://acm.nyist.net/JudgeOnline/problem.php?pid=604 小明的难题 时间限制:1000 ms  |  内存限制:65535 KB 难度:1   描述 ...

随机推荐

  1. 单独编译一个ext4内核模块

    当我们需要使用一个内核模块的时候,在当前使用版本内核编译的时候又没有加进去,在不改变内核版本的时候,再编译整个内核,可能会覆盖原来的内核,导致系统无法启动 现在我们能够单独选择需要的模块,然后加载进内 ...

  2. CSP-J 2020题解

    CSP-J 2020题解 本次考试还是很有用的,至少把我浇了一盆冷水. 当使用民间数据自测的时候,我就自闭了. 估分是320,但有些比较低级的错误直接少掉80. 而且这套题应该上350才正常吧,也不是 ...

  3. maven项目导出为jar包

    1:ctrl + R  输入cmd 2:切换路径到自己的项目路径下 3:执行--> mvn assembly:assembly ( 若显示编码问题: 查看编码方式:chcp 修改编码:chcp ...

  4. 从执行上下文角度重新理解.NET(Core)的多线程编程[1]:基于调用链的”参数”传递

    线程是操作系统能够进行运算调度的最小单位,操作系统线程进一步被封装成托管的Thread对象,手工创建并管理Thread对象已经成为了所能做到的对线程最细粒度的控制了.后来我们有了ThreadPool, ...

  5. Mac插件太多太乱怎么办?CleanMyMac直接帮你搞定!

    电脑应用插件在一定程度上便利了大家的生活,保障了用户的使用安全,比如Flash插件.浏览器翻译插件.银行安全登录插件等等.但是许多的插件并不能定位安装的位置,同时部分插件,大部分时候都是只使用一次的, ...

  6. Wasp XT合成器功能介绍

    本章节将采用图文结合的方式给大家讲解电音编曲软件FL Studio中的Wasp XT合成器的相关功能,感兴趣的朋友可以一起来交流哦. 下面我们一起来看看吧 Wasp XT是一个3振荡器合成器,它包含一 ...

  7. 关于Camtasia2020安装完成之后无法运行问题的解决方法

    在录像编辑软件Cmtasia更新到了2020版本之后,有部分小伙伴们遇到了这样的问题:在我们安装好软件之后,居然无法运行.今天小编就给大家介绍一下该如何解决这个问题. 方法一: 第一步,选中桌面上Ca ...

  8. 实时检测微信域名防红拦截检测API系统,最新腾讯域名屏蔽检测官方接口

    最近手里有个项目需要检测域名在微信里是否可以打开,如果被微信拦截,则需要进行下一步操作,所以需要判断域名的状态,但是微信官方并没有提供相关查询的方法,最后在网上找到了这个接口地址,分享给有需要的朋友. ...

  9. MySql学习笔记--详细整理--下

    目录 索引 测试索引 索引原则 权限管理和备份 备份 规范数据库设计 三大范式 JDBC JDBC程序 Statement对象 工具类实现 sql注入 PreparedStatement对象 事务 数 ...

  10. 牛客练习赛69 火柴排队 题解(dp)

    题目链接 题目大意 给你一个长为n(n<=5e3)的数组a.随机使得k个元素增加d.要你求多大的概率使得,这些数组元素的相对大小不发生改变 输出 n 行每行一个整数,第 i 行的整数表示 k=i ...