no crossing
c++代码
给出一个有向图,找一条恰好经过 k 个点的最短路径,要求每次选的边不能跃过之前已经经过的节点。即对于路径中的边x→y ,不存在以前经过的点 tt 使得三者的编号满足 min(x,y)≤t≤max(x,y) 。
输入格式
第一行三个数字 n,k,mn,k,m。
接下来mm行 , 每行 3 个整数ai,bi,ci表示存在一条从 ai→bi , 长度为 ci 的有向边。
输出格式
一个数,表示答案。如果不存在任何一条路径满足条件,则输出 −1。
样例1输入
7 4 4
1 6 2
6 2 2
2 4 2
2 7 1
样例1输出
6
样例2输入
4 3 4
2 1 2
1 3 2
3 4 2
4 1 1
样例2输出
3
数据规模
所有数据保证 1≤n,k≤100,0≤m≤2000,1≤ai,bi≤n,1≤ci≤1000,1≤n,k≤100,0≤m≤2000,1≤ai,bi≤n,1≤ci≤1000,。
Note
样例一的最短路径为 1→6→2→4 。1→6→2→7是不正确的,因为 2<6<7。
#include<bits/stdc++.h>
using namespace std;
int e[201][201];
int n,kk,m;
const int INF = 1e9;
int dp[101][101][101][3];
//dp[l,r,k][0/1]表示可取范围为lr,已经走过k个点 ,现在在右/左端点(l=1;r=0)
int main()
{
ios::sync_with_stdio(false);
cin>>n>>kk>>m;
for(int i=1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
if(e[a][b]==0) e[a][b]=c; else e[a][b]=min(e[a][b],c);
}
if(kk>n){//特判,不一定有用上
cout<<-1<<endl;
return 0;
}
for(int i=0;i<101;i++){
for(int j=i;j<101;j++){
for(int k=0;k<101;k++){
dp[i][j][k][0] = dp[i][j][k][1] = INF;
}
}
}
//初始化必须要
for(int i=0;i<101;i++){
for(int j=i;j<101;j++){
dp[i][j][kk][0]=0;
dp[i][j][kk][1]=0;
}
}
for(int k=kk;k>0;k--)//倒着扩张
{
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++)
{
for(int x=1;x<l;x++)/
{
if(e[x][l])
{
dp[x][r][k-1][1]=min(dp[l][r][k][1]+e[x][l],dp[x][r][k-1][1]);
}
if(e[x][r])
{
dp[x][r][k-1][1]=min(dp[l][r][k][0]+e[x][r],dp[x][r][k-1][1]);
}
}
for(int x=r+1;x<=n;x++)/
{
if(e[x][r])
{
dp[l][x][k-1][0]=min(dp[l][r][k][0]+e[x][r],dp[l][x][k-1][0]);
}
if(e[x][l])
{
dp[l][x][k-1][0]=min(dp[l][r][k][1]+e[x][l],dp[l][x][k-1][0]);
}
}
}
}
int ans = INF;
for(int l=1;l<=n;l++)
{
for(int r=l;r<=n;r++)
{
if(dp[l][r][1][0]!=0)ans=min(ans,dp[l][r][1][0]);
if(dp[l][r][1][1]!=0)ans=min(ans,dp[l][r][1][1]);
}
}
if(ans<INF) cout<<ans<<endl;else cout<<-1<<endl;
return 0;
}