区间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));
}
printf("%lld\n",min(dp[1][n][1],dp[1][n][0])*V);
}
return 0;
}
//所以区间dp不要被大多数情况给迷惑了,还是要具体问题具体分析的