最近做DP做得有点多。。技巧和思路是提升了不少。但是还是会忽略不少细节,特别是dfs+dp的题目上。比如说这题,就出现了两个足以让人窒息的错误:1、数组d的值可能会是我初始化的负数,所以返回再求最小,必然是输出-1,所以需要在末尾加上一个判断它的正负。2、第二个就是数组下标的越界的导致的re问题,要时刻注意数组的下标是否可以取得到,特别是未初始化的值做下表时。
思路:这道题开始简单地以为是DAG上的DP,后来打的时候发现,这个又要复杂一点。思路也是参考网上的。。。
题目给出了n个节点,以及一组路径,问:经过k个点的最短距离是多少,要求下一次走的路径不能完全覆盖上一次的路径。
首先n个节点可以看做是对应数轴上的点,每次走一段路径后,就要更新下次的合法距离。开始的合法距离注意要是0~n+1。
分三种情况讨论:
1、如果下次进入的点在合法距离以外则舍弃掉该点。
2、如果在合法距离内:a、大于当前位置,则合法距离的左边界应为当前位置。b、小于当前位置,则合法距离的右边界应为当前位置。具体可以画图理解。
然后就是dfs每个节点,找出所有情况了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=82;
int n,m,k,w,G[maxn][maxn],d[maxn][maxn][maxn][maxn],st,en,mins,ans;//d[l][r][st][k];从第
int dfs(int st,int step,int l,int r){//从st出发,剩余step步,范围为l r的最短步长
if(d[l][r][st][step]!=-1)
return d[l][r][st][step];///已经得出来答案就不需要再进行
if(step==1){
return d[l][r][st][step]=0;///递归走到第一步
}
for(int i=1;i<=n;i++){///递归st的所有儿子
if(G[st][i]!=inf&&i>l&&i<r){
int l1=l,r1=r;
if(i>st)
l1=st;
else
r1=st;
int ans=dfs(i,step-1,l1,r1);
if(d[l][r][st][step]>0){
d[l][r][st][step]=min(ans+G[st][i],d[l][r][st][step]);
}
else
d[l][r][st][step]=ans+G[st][i];
}
}
if(d[l][r][st][step]>0)
return d[l][r][st][step];
return inf;
}
int main(){
while(cin>>n>>k>>m){
memset(d,-1,sizeof(d));
memset(G,inf,sizeof(G));
mins=inf;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&st,&en,&w);
G[st][en]=min(G[st][en],w);///考虑有重边的情况
}
for(int i=1;i<=n;i++){
mins=min(mins,dfs(i,k,0,n+1));
}
if(mins>=inf)
mins=-1;
printf("%d\n",mins);
}
return 0;
}