区间dp就是我知道区间长度为len-1的所有状态,然后我可以通过小于len的的状态转移到区间长度为len的状态
一般是在外层循环遍历len,内层循环遍历起点来做的
但是这次做了一个很特别的题目
题目描述:
在x轴上有n个客人叫外卖,每个顾客因为追的番更新进度不同,所以在等外买的时间里每秒增加的愤怒值不同。给出客人和餐厅的位置,以及客人每分钟增加的愤怒值,还有快递小哥的行走一公里需要的时间。问送完外卖后n个客人的最小愤怒值?
解题思路:
区间DP,大一的时候做省赛练习的时候见过这种类型的题目,但是今天遇到由于年代久远,还是GG······。
把餐厅所在的点加进去,然后按照在x轴上的位置排序。从餐厅所在位置向左右开始DP,dp[x][y][z] 代表 处理完区间[y, z]停留在x方向的最小花费,这个题目要预处理未加进去点的花费。然后选取最优。
zoj 3469
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #define maxn 1100 #define ll long long using namespace std; struct Data { ll x,p; }; bool cmp(Data a,Data b) { return a.x<b.x; } Data a[maxn]; ll dp[maxn][maxn][3]; ll sum[maxn]; int main() { ll n,V,X; while(~scanf("%lld %lld %lld",&n,&V,&X)) { for (ll k=1;k<=n;k++) scanf("%lld %lld",&a[k].x,&a[k].p); a[n+1].x=X;a[n+1].p=0; n++; sort(a+1,a+n+1,cmp); sum[0]=0; for (ll k=1;k<=n;k++) sum[k]=sum[k-1]+a[k].p; for (ll i=0;i<=n;i++) for (ll j=0;j<=n;j++) dp[i][j][0]=dp[i][j][1]=0x3f3f3f3f; ll res; for (ll k=1;k<=n;k++) if (a[k].x==X) {dp[k][k][0]=dp[k][k][1]=0;res=k;break;} for (ll i=res;i>=1;i--) for (ll j=res;j<=n;j++) { if (i==j) continue; if (i == j) continue; dp[i][j][0] = min (dp[i][j][0], dp[i+1][j][0]+(sum[n]-sum[j]+sum[i])*(a[i+1].x - a[i].x));//这个其实特别奇妙,就是
//我其实算值的增量的时候算的是
//区间外的增量
dp[i][j][0] = min (dp[i][j][0], dp[i+1][j][1]+(sum[n]-sum[j]+sum[i])*(a[j].x - a[i].x));
dp[i][j][1] = min (dp[i][j][1], dp[i][j-1][1]+(sum[n]-sum[j-1]+sum[i-1])*(a[j].x - a[j-1].x));
dp[i][j][1] = min (dp[i][j][1], dp[i][j-1][0]+(sum[n]-sum[j-1]+sum[i-1])*(a[j].x - a[i].x));
}
//所以区间dp不要被大多数情况给迷惑了,还是要具体问题具体分析的printf("%lld\n",min(dp[1][n][1],dp[1][n][0])*V); } return 0; }