先是流水账
报名了camp的div1,原本说是cf能1500以上就div1来着?但是这每日一题感觉都不是很友好,每天会花很长时间去做,而且理解还不深刻,但是既然已经做了,怎么能白白花费时间呢,所以还是要写个题解记录一下。
题解
题意:
给定一个有向有权图(图中会有重边,环)。对于每个节点都有自己的编号,询问一条包含k个节点路径的最小权值,并且有特殊要求如下:对于将行路径x—>y没有已经走过的节点编号 t 满足:min(x,y)=<t<=max(x,y)
分析:
1.根据题目数据范围,我们可以采用vector或者邻接矩阵存储图。
2.对于题目的特殊要求min(x,y)=<t<=max(x,y),翻译过来就是,x向下走到y,对y必须有在之前走过的区间之内,1 2 6 7是不合法的路径,因为走7时越过了已经走过的节点6(可以用数轴表示能看的直观一些)
3.根据2可知每一次走完可走区间都会缩小,并且小区间不会影响大区间,根据这两点特性可以锁定是区间DP做法,也可用不断缩小上下界的BFS来做。
下为记忆化搜索代码,建议从main函数开始看
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int>p;
vector<p> v[105];
int dp[110][110][110][110];//利用四个维度来表示:可行区间为l到r,目前决策点为第三维度表示,第四维度表示目前记录了多少路径
int n,m,k;
int DFS(int x,int y,int u,int cnt)
{
if(cnt==k)//路径记录终了
return 0;
if(dp[x][y][u][cnt])//已经搜过的情况立即返回即可
return dp[x][y][u][cnt];
int &res=dp[x][y][u][cnt];
res=1e9;
for(auto f:v[u])//遍历u所连接的节点
{
if(f.first<y&&f.first>x&&f.first!=u)
{
if(u>f.first)
res=min(res,f.second+DFS(x,u,f.first,cnt+1));
else
res=min(res,f.second+DFS(u,y,f.first,cnt+1));
}
}
return res;
}
int main()
{
cin>>n>>k>>m;
for(int i=1,u,r,w;i<=m;i++)
{
scanf("%d%d%d",&u,&r,&w);
v[u].push_back(p(r,w));
}
int ans=1e9;
for(int i=1;i<=n;i++)
{
ans=min(ans,DFS(0,n+1,i,1));
}
if(ans==1e9)
cout<<-1<<endl;
else
cout<<ans<<endl;
return 0;
}