AtCoder Beginner Contest 338F - Negative Traveling Salesman【floyd+状态压缩dp】

本文主要是介绍AtCoder Beginner Contest 338F - Negative Traveling Salesman【floyd+状态压缩dp】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原题链接:https://atcoder.jp/contests/abc338/tasks/abc338_f

Time Limit: 6 sec / Memory Limit: 1024 MB

Score: 500 points、

问题陈述

有一个有N个顶点和M条边的加权简单有向图。顶点的编号为 1 到 N,i/th 边的权重为 Wi​,从顶点 Ui​ 延伸到顶点Vi​。权重可以为负,但该图不包含负循环。

确定是否存在至少访问每个顶点一次的行走。如果存在这样的行走,请找出所遍历的边的最小总权重。如果同一条边被多次遍历,则每次遍历都要加上这条边的权重。

这里的 "至少访问每个顶点一次的行走 "是指满足以下两个条件的顶点序列v1​,v2​,…,vk​:

  • 对于每个 i 有一条边从顶点 vi​ 延伸到顶点 vi+1​。
  • 对于每个 j (1≤j≤N) 都有 i (1≤i≤k) 这样的边 vi​=j 。

限制因素

  • 2≤N≤20
  • 1≤M≤N(N−1)
  • 1≤Ui​,Vi​≤N
  • Ui​\neqVi​
  • (Ui​,Vi​)\neq(Uj​,Vj​)
  • −1e6≤Wi​≤1e6
  • 给定图形不包含负循环。
  • 所有输入值均为整数。

输入输出描述

Sample Input 1Copy

3 4
1 2 5
2 1 -3
2 3 -4
3 1 100

Sample Output 1Copy

-2

按照 2→1→2→3 的顺序跟随顶点,可以访问所有顶点至少一次,所遍历的边的总重量为 (−3)+5+(−4)=−2。这是最小值。

Sample Input 2Copy

3 2
1 2 0
2 1 0

Sample Output 2Copy

No

没有至少访问所有顶点一次的行走。

Sample Input 3Copy

5 9
1 2 -246288
4 5 -222742
3 1 246288
3 4 947824
5 2 -178721
4 3 -947824
5 4 756570
2 5 707902
5 1 36781

Sample Output 3Copy

-449429

解题思路:

首先题目要求我们至少经过每个点一次,也就是说我们只要找到一条路径经过了每个点并且这条路径权值总和最小,也就是说我们并不关心每个点经过了几次,只要经过了就行了,例如如果我们找到了一条权值总和最小并且经过所有点至少一次的路径a->b->c->b->d,我们考虑的路径集合应该是a->b->c->d,,也就是说c->b->d中间的b只是c->d最短路径经过的一个点而已,也就是说我们只需要考虑走过每个点的顺序即可,确定了顺序之后,任意俩个点之间的距离应该使用最短路径,由于不存在负环,任意俩个点之间肯定是存在最短路径的,并且最短路径的长度肯定是确定的,这个题目点的个数最多只有20个,对于考虑哪种走的顺序总路径最短,我们可以考虑状态压缩dp,然后需要提前预处理任意俩个点之间的最短路,可以采用floyd算法才求任意俩点之间的最短路径,预处理之后状态压缩dp处理即可,具体分析见代码处。

状态压缩dp处理过程

状态定义:

定义f[i][j]表示当前走的点的集合为i,并且走过的最后一个点为j的所有方案的最短路径。

初始化:

f[1<<i][i]=0,当前集合只有一个点,就是起点。

状态转移:

当前走过的最后一个点是j,需要考虑倒数第二个点k,用来转移

f[i][j]=min(f[i^(1<<j][k]+d[k][j])

最终答案:

最终走过的点的集合肯定是(1<<n)-1,然后走过的最后一个点可以是任意一个点,所以答案就是min(f[(1<<n)-1][i]) (0<i<n)

时间复杂度:floyd预处理时间复杂度为O(n^3),n=20,这个时间大概是8e3,状态压缩dp处理时间复杂度为O((2^n)*n*n),这个时间大概为4e8,这个时间复杂度很高了,但是这个题目给了6s的时间,所以是可以过的,最终是时间复杂度为O((n^3)+(2^n)*n*n)。

空间复杂度:d数组空间为O(n^2),dp数组f空间为O((2^n)*n),最终空间复杂度为O((n^2)+(2^n)*n),空间大概是(20^2+(2^20)*20),大概是2e7*4/1e6=80M,这个题目给了1024M,空间肯定是足够的。

cpp代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>using namespace std;const int N = 25, M = 1 << 20, INF = 0x3f3f3f3f;int n, m;
int d[N][N], f[M][N];void floyd()
{for (int k = 0; k < n; k++)for (int i = 0; i < n; i++)for (int j = 0; j < n; j++){if (d[i][k] == INF || d[k][j] == INF)continue;d[i][j] = min(d[i][j], d[i][k] + d[k][j]);}
}
int main()
{cin >> n >> m;for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)if (i == j)d[i][j] = 0;elsed[i][j] = INF;for (int i = 0; i < m; i++){int u, v, w;scanf("%d%d%d", &u, &v, &w);u--, v--;d[u][v] = w;    //题目说了不存在重边。所以这里可以直接赋值,如果存在重边应该取所有重边的最小值}//floyd预处理任意俩个点之间的最短路floyd();memset(f, 0x3f, sizeof f);//初始之起点for (int i = 0; i < n; i++)f[1 << i][i] = 0;//状态压缩dp处理过程for (int i = 0; i < 1 << n; i++)  //第一维枚举走过的点的集合for (int j = 0; j < n; j++)   //第二维枚举当前走的点的集合中的最后一个点{if (~i >> j & 1)    //当前最后一个点是j,那么当前走过的点的集合必须包含j,continue;for (int k = 0; k < n; k++){if (j == k)   //不可能自己走到自己continue;if (~i >> k & 1) //前一个点是k,所以当前走过的点的集合i中必须包含kcontinue;if (f[i ^ (1 << j)][k] == INF || d[k][j] == INF)  //INF表示某种状态不存在或者俩个点之间不存在边,不能用于转移continue;f[i][j] = min(f[i][j], f[i ^ (1 << j)][k] + d[k][j]);}}int ans = INF;//当前走过的点的集合肯定是(1<<n)-1,最后一个点可以是任意一个点for (int i = 0; i < n; i++)ans = min(ans, f[(1 << n) - 1][i]);if (ans == INF)   //不存在走过每个点至少一次的路径,输出Noputs("No");else         //存在走过每个点至少一次的路径,输出最短路径的长度cout << ans << endl;return 0;
}

这篇关于AtCoder Beginner Contest 338F - Negative Traveling Salesman【floyd+状态压缩dp】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/663268

相关文章

模型压缩综述

https://www.cnblogs.com/shixiangwan/p/9015010.html

HTTP状态码中301与302的区别

一.官方说法  301,302 都是HTTP状态的编码,都代表着某个URL发生了转移,不同之处在于:  301 redirect: 301 代表永久性转移(Permanently Moved)。  302 redirect: 302 代表暂时性转移(Temporarily Moved )。  这是很官方的说法,那么它们的区别到底是什么呢?  1.1、什么是301转向?什么是301重定向?

上海邀请赛 A题目 HDU 5236(dp)

先求出没有ctrl+s的时候构造长度为i的期望f[i] 。然后枚举保存的次数,求出最小即可。 #include<cstdio>#include<cstdio>#include<cmath>#include<queue>#include<stack>#include<string>#include<cstring>#include<iostream>#include<map>

poj 3160 Father Christmas flymouse 强连通+dp

首先我们可以确定的是,对于val值小于0的节点都变成0.   假设一个集合内2个房间都能任意到达,那么我就可以吧集合内的所有点的价值都取到,并且可以达到任一点。实际上集合内的每个点是相同的,这样的集合就是一个强连通分量。 那么我们就可以用tarjin算法进行强连通缩点, 最后形成一个dag的图。在dag的图上面进行dp。可以先用拓扑排序后dp。或者建反响边记忆化搜索 。 VIEW

秋招突击——6/22——复习{区间DP——加分二叉树,背包问题——买书}——新作{移除元素、实现strStr()}

文章目录 引言复习区间DP——加分二叉树个人实现 背包问题——买书个人实现参考实现 新作移除元素个人实现参考思路 找出字符串中第一个匹配项的下标个人实现参考实现 总结 引言 今天做了一个噩梦,然后流了一身汗,然后没起来,九点多才起床背书。十点钟才开始把昨天那道题题目过一遍,然后十一点才开始复习题目,为了不耽误下午的时间,所以这里的就单纯做已经做过的题目,主打一个有量,不在学

Zustand 状态管理库简介

1. Zustand 简介 Zustand(德语中意为“状态”)是一个使用简单 API 的 React 状态管理库。它的核心思想是以状态切片(slices)的方式组织应用状态,从而实现高效的状态管理。Zustand 提供了比 Redux 更加简洁和直接的用法,同时支持异步操作和中间件。 在React开发中,状态管理是一个非常重要的概念。虽然 React 提供了 useState 和 useRe

Codeforces April Fools Day Contest 2014(附官方题解)

Codeforces2014年愚人节的坑题。。。但还是感觉挺好玩的。。。 A. The Great Game time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Two teams mee

Codeforces April Fools Day Contest 2013

2013年愚人节的坑题。。。 A. Mysterious strings time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Input The input contains a sin

ESP32使用按键配网并通过LED指示网络状态

前言 上面我们已经可以通过 ESPTOUCH 和 Airkiss 给模块配网,并且存储在 nvs 中,重启后仍然可以联网,只是这样仍然不能满足我们实际的应用,这次我们增加按键作为输入,LED作为输出,实现长按按键配网,并可以通过LED指示网络状态。 添加自己的组件 为了让程序结构更加清晰,所以我们在smart_config例程的基础上做了修改,在main文件夹里新建了main.c 、smar

atcoder ABC 359-B题详解

atcoder ABC 359-B题详解 Problem Statement There are 2N people standing in a row, and the person at the i-th position from the left is wearing clothes of color Ai. Here, the clothes have N colors from 1