P3842-DP【黄】

发布时间 2023-11-11 16:29:46作者: Kai-G

想搜索到最后一层,就必得先完成前面层的搜索任务,这构成了对状态转移的启示,即当前层的DP值应该是此前层转移过来后得到的最佳值。
但这道题看数据范围应该不能用二维数组,抱着侥幸的心理我使用了动态二维数组,dpij表示以第i层第j个为终点时的答案,结果MLE了,喜提42分,详见CODE-1。

后来意识到(其实是瞥了一眼题解后立刻受启发,然后想到的...)从中间节点转移过去是等价于先从边界节点动到中间节点在下去移动的,换句话说不需要存储、记录、判断那么多的状态,只需要存2*n个值就可以,但好像我写假了,不知为什么只有七十多分,详见CODE-2

总结,用记搜思想或者“想搜索到最后一层,就必得先完成前面层的搜索任务”等方法判断出所需的DP的下标的含义,然后在写出状态转移方程,这是我自己归纳的思考DP问题的一种比较简单的思维路径,应该多用用

CODE-1

#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <unordered_map>
#include <cmath>
//#define int long long
using namespace std;

int * dp[20000+2];
int n,L[20000+2],R[20000+2];
int dfs(int row,int col)
{
	if(col<L[row]||col>R[row])return 0;
	if(dp[row][col-L[row]]!=-1)return dp[row][col-L[row]];
	if(row==1)return dp[row][col-L[row]]=R[row]-1+R[row]-col;
	int ANS=99999999;
	for(int i=L[row-1];i<=R[row-1];i++)
	{
		if(i<=R[row]&&i>=L[row])
			ANS=min(ANS,dfs(row-1,i)+1+2*(R[row]-max(i,col))+2*(min(i,col)-L[row])+max(i,col)-min(i,col));
		else if(i>R[row]) ANS=min(ANS,dfs(row-1,i)+1+i-L[row]+col-L[row]);
		else if(i<L[row]) ANS=min(ANS,dfs(row-1,i)+1+R[row]-i+R[row]-col);
	}
	return dp[row][col-L[row]]=ANS;
}
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>L[i]>>R[i];
		dp[i]=new int[R[i]-L[i]+1];
		for(int j=0;j<R[i]-L[i]+1;j++)dp[i][j]=-1;//li~ri->0~ri-li
	}
	cout<<n-R[n]+dfs(n,R[n])<<endl;
	return 0;
}
	/*
	dfs(n,R[n]);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			printf("%3d",dfs(i,j));
		}
		cout<<endl;
	}*/

CODE-2

#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <unordered_map>
#include <cmath>
#define int long long
using namespace std;

int dp[20000+2][3];
int n,L[20000+2],R[20000+2];
int dfs(int row,int col)//now col is left or right
{
	if(dp[row][col]!=-1)return dp[row][col];
	int ANS=1e16;

	if(col==1)//towards L[row]
	{
		//from L[row-1]
		if(L[row-1]<=R[row])
			ANS=min(ANS,dfs(row-1,1)+1+R[row]-L[row-1]+R[row]-L[row]);
		else 
			ANS=min(ANS,dfs(row-1,1)+1+L[row-1]-L[row]);
		//from R[row-1]
		if(R[row-1]<=R[row])
			ANS=min(ANS,dfs(row-1,2)+1+R[row]-R[row-1]+R[row]-L[row]);
		else 
			ANS=min(ANS,dfs(row-1,2)+1+R[row-1]-L[row]);
	}
	else//towards R[row]
	{
		//from L[row-1]
		if(L[row-1]<=L[row])
			ANS=min(ANS,dfs(row-1,1)+1+R[row]-L[row-1]);
		else 
			ANS=min(ANS,dfs(row-1,1)+1+L[row-1]-L[row]+R[row]-L[row]);
		//from R[row-1]
		if(R[row-1]<=L[row])
			ANS=min(ANS,dfs(row-1,2)+1+R[row]-R[row-1]);
		else 
			ANS=min(ANS,dfs(row-1,2)+1+R[row-1]-L[row]+R[row]-L[row]);
	}
	for(int i=L[row-1];i<=R[row-1]/2;i++)
	{
		
		if(i<=R[row]&&i>=L[row])
			ANS=min(ANS,dfs(row-1,2)+R[row-1]-i+1+2*(R[row]-max(i,col))+2*(min(i,col)-L[row])+max(i,col)-min(i,col));
		else if(i>R[row]) ANS=min(ANS,dfs(row-1,2)+1+i-L[row]+col-L[row]);
		else if(i<L[row]) ANS=min(ANS,dfs(row-1,2)+1+R[row]-i+R[row]-col);
	}
		
	return dp[row][col]=ANS;
}
signed main()
{
	scanf("%ld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%ld%ld",&L[i],&R[i]);
		for(int j=0;j<3;j++)dp[i][j]=-1;//li~ri->0~ri-li
	}
	dp[1][1]=R[1]-1+R[1]-L[1];
	dp[1][2]=R[1]-1;
	cout<<min(n-R[n]+dfs(n,2),n-L[n]+dfs(n,1))<<endl;
	return 0;
}
	/*
	dfs(n,R[n]);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			printf("%3d",dfs(i,j));
		}
		cout<<endl;
	}*/