【BZOJ-4386】Wycieczki DP + 矩阵乘法
4386: [POI2015]Wycieczki
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 197 Solved: 49
[Submit][Status][Discuss]
Description
给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种。
将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。
Input
第一行包含三个整数n,m,k(1<=n<=40,1<=m<=1000,1<=k<=10^18)。
接下来m行,每行三个整数u,v,c(1<=u,v<=n,u不等于v,1<=c<=3),表示从u出发有一条到v的单向边,边长为c。
可能有重边。
Output
包含一行一个正整数,即第k短的路径的长度,如果不存在,输出-1。
Sample Input
6 6 11
1 2 1
2 3 2
3 4 2
4 5 1
5 3 1
4 6 3
1 2 1
2 3 2
3 4 2
4 5 1
5 3 1
4 6 3
Sample Output
4
HINT
长度为1的路径有1->2,5->3,4->5。
长度为2的路径有2->3,3->4,4->5->3。
长度为3的路径有4->6,1->2->3,3->4->5,5->3->4。
长度为4的路径有5->3->4->5。
Source
Solution
边权只有1,2,3三种,可以考虑拆点,那么只有边权为1的边了
那么显然可以DP,但是时间复杂度不允许
考虑用矩阵乘法去转移
这里比较优秀的方法是基于倍增的矩阵乘法
总复杂度是O(n3logK)
答案会很大,乘爆longlong需要特判
这题细节非常多!
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; inline long long read() { long long x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 130 int id[MAXN][4],ID,N,M,B; long long K,V[MAXN*3]; struct MatrixNode{long long a[MAXN][MAXN];}a[100],b,c,tmp; MatrixNode operator * (const MatrixNode &A,const MatrixNode &B) { MatrixNode C; memset(C.a,0,sizeof(C.a)); for (int i=0; i<=ID; i++) for (int j=0; j<=ID; j++) for (int k=0; k<=ID; k++) if (A.a[i][k]&&B.a[k][j]) { if (A.a[i][k]<0 || B.a[k][j]<0) {C.a[i][j]=-1; break;} if (A.a[i][k]>K/B.a[k][j]) {C.a[i][j]=-1; break;} C.a[i][j]+=A.a[i][k]*B.a[k][j]; if (C.a[i][j]>K) {C.a[i][j]=-1; break;} } return C; } bool Check() { long long re=0; for (int i=0; i<=ID; i++) if (tmp.a[0][i] && V[i]) { if (tmp.a[0][i]<0) return 0; if (tmp.a[0][i]>K/V[i]) return 0; re+=tmp.a[0][i]*V[i]; if (re>K) return 0; } return re<K; } int main() { N=read(),M=read(),K=read(); for (int i=1; i<=N; i++) for (int j=0; j<=2; j++) id[i][j]=++ID; for (int i=1; i<=N; i++) { for (int j=0; j<=1; j++) a[0].a[id[i][j]][id[i][j+1]]++; a[0].a[0][id[i][0]]++; } a[0].a[0][0]++; for (int x,y,z,i=1; i<=M; i++) x=read(),y=read(),z=read(),a[0].a[id[y][z-1]][id[x][0]]++,V[id[y][z-1]]++; for (int i=0; (1LL<<i)<=K*3; B=++i); B--; for (int i=1; i<=B; i++) a[i]=a[i-1]*a[i-1]; long long ans=0; for (int i=0; i<=ID; i++) c.a[i][i]=1; for (int i=B; ~i; i--) { tmp=c*a[i]; if (Check()) ans|=(1LL<<i),memcpy(c.a,tmp.a,sizeof(tmp.a)); } ans++; if (ans>K*3) {puts("-1"); return 0;} else printf("%lld\n",ans); return 0; }
模拟赛的时候,没认真读题,以为是“魔法猪学院”类似的...等到半场才发现.....然后GG
——It's a lonely path. Don't make it any lonelier than it has to be.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述