【DP】模型:旅行商简化版

题型概述

给出一个图,需要从起点 A A A出发,走到终点 B B B。再从终点 B B B出发,走到起点 A A A。(两次路径经过的点不能重复)求路径最小值。

模型构建

状态设计

从某一个点出发,设 d p ( i , j ) dp(i,j) dp(i,j)表示从 i i i走到起点 A A A,再从起点 A A A出发走到 j j j所花费的最小值。

状态转移

我们用 d p ( i , j ) dp(i,j) dp(i,j)来更新下一个点 m a x ( i , j ) + 1 max(i,j)+1 max(i,j)+1
不难得出
d p [ i ] [ k ] = m i n ( d p [ i ] [ k ] , d p [ i ] [ j ] + d i s ( j , k ) ) ; dp[i][k]=min(dp[i][k],dp[i][j]+dis(j,k)); dp[i][k]=min(dp[i][k],dp[i][j]+dis(j,k));
d p [ k ] [ j ] = m i n ( d p [ k ] [ j ] , d p [ i ] [ j ] + d i s ( i , k ) ) ; dp[k][j]=min(dp[k][j],dp[i][j]+dis(i,k)); dp[k][j]=min(dp[k][j],dp[i][j]+dis(i,k));

其中, d i s ( i , k ) dis(i,k) dis(i,k)为点 i i i到点 k k k的欧几里得距离。
理解如下:
在这里插入图片描述
k k k i i i组合时,有: d p [ k ] [ j ] = m i n ( d p [ k ] [ j ] , d p [ i ] [ j ] + d i s ( i , k ) ) ; dp[k][j]=min(dp[k][j],dp[i][j]+dis(i,k)); dp[k][j]=min(dp[k][j],dp[i][j]+dis(i,k));

k k k j j j组合时,有: d p [ i ] [ k ] = m i n ( d p [ i ] [ k ] , d p [ i ] [ j ] + d i s ( j , k ) ) ; dp[i][k]=min(dp[i][k],dp[i][j]+dis(j,k)); dp[i][k]=min(dp[i][k],dp[i][j]+dis(j,k));

边界条件

i i i到终点 B B B时,直接有: d p [ B ] [ B ] = m i n ( d p [ B ] [ B ] , d p [ B ] [ j ] + d i s ( j , B ) ) ; dp[B][B]=min(dp[B][B],dp[B][j]+dis(j,B)); dp[B][B]=min(dp[B][B],dp[B][j]+dis(j,B));
j j j到终点 B B B时,直接有: d p [ B ] [ B ] = m i n ( d p [ B ] [ B ] , d p [ i ] [ n ] + d i s ( i , B ) ) ; dp[B][B]=min(dp[B][B],dp[i][n]+dis(i,B)); dp[B][B]=min(dp[B][B],dp[i][n]+dis(i,B));
我们令 d p ( i , j ) = i n f dp(i,j)=inf dp(i,j)=inf, d p ( 1 , 1 ) = 0 dp(1,1)=0 dp(1,1)=0

典例变形

T1:P1523 旅行商简化版

最基本的模板,直接上代码

#include 
#define inf (double)1e18+7
#define ll long long
using namespace std;
const ll maxn=1005;
template <typename t>void read(t &x)
{ll f=1;x=0;char ch=getchar();while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}x=x*f;
}
struct NODE
{ll x,y;
}node[maxn];
ll n,b1,b2;
double dp[maxn][maxn];double dis(ll a,ll b)
{return sqrt((node[a].x-node[b].x)*(node[a].x-node[b].x)+(node[a].y-node[b].y)*(node[a].y-node[b].y));
}bool cmp(NODE a,NODE b)
{return a.x<b.x;
}void init()
{freopen("B.in","r",stdin);
}void readdata()
{read(n);for(ll i=1;i<=n;i++){read(node[i].x),read(node[i].y);}
}void work()
{sort(node+1,node+n+1,cmp);for(ll i=1;i<=n;i++){for(ll j=1;j<=n;j++){dp[i][j]=inf;}}dp[1][1]=0;for(ll i=1;i<=n;i++){for(ll j=1;j<=n;j++){if(i!=j || i==1){ll k=max(i,j)+1;if(k==n+1){				if(i==n)dp[n][n]=min(dp[n][n],dp[n][j]+dis(j,n));if(j==n)dp[n][n]=min(dp[n][n],dp[i][n]+dis(i,n));}else {											dp[i][k]=min(dp[i][k],dp[i][j]+dis(j,k));dp[k][j]=min(dp[k][j],dp[i][j]+dis(i,k));}}}}printf("%.2lf",dp[n][n]);
}int main()
{
//	init();readdata();work();return 0;
}
T2:最短路径(MZOJ上将会出现)

在这里插入图片描述这道题在T1基础上加了第三个问。
如何做到第一条路径必须经过 b 1 b1 b1而第二条路径必须经过 b 2 b2 b2
我们不妨换个思路
第一条路径必须经过 b 1 b1 b1等价于第二条路径不能经过 b 1 b1 b1
同理
第二条路径必须经过 b 2 b2 b2等价于第一条路径不能经过 b 2 b2 b2

所以我们在T1的基础上把代码稍稍修改一下:
i f ( k ! = b 1 ) d p [ i ] [ k ] = m i n ( d p [ i ] [ k ] , d p [ i ] [ j ] + d i s ( j , k ) ) ; if(k!=b1) dp[i][k]=min(dp[i][k],dp[i][j]+dis(j,k)); if(k!=b1)dp[i][k]=min(dp[i][k],dp[i][j]+dis(j,k));
i f ( k ! = b 2 ) d p [ k ] [ j ] = m i n ( d p [ k ] [ j ] , d p [ i ] [ j ] + d i s ( i , k ) ) ; if(k!=b2)dp[k][j]=min(dp[k][j],dp[i][j]+dis(i,k)); if(k!=b2)dp[k][j]=min(dp[k][j],dp[i][j]+dis(i,k));
所以问题就迎刃而解啦:

#include 
#define inf (double)1e8+7
using namespace std;
const int maxn=1005;
template <typename t>void read(t &x)
{int f=1;x=0;char ch=getchar();while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}x=x*f;
}
struct NODE
{int x,y;
}node[maxn];
int n,b1,b2;
double dp[maxn][maxn];double dis(int a,int b)
{return sqrt((node[a].x-node[b].x)*(node[a].x-node[b].x)+(node[a].y-node[b].y)*(node[a].y-node[b].y));
}void init()
{freopen("B.in","r",stdin);
}void readdata()
{read(n),read(b1),read(b2);b1++,b2++;for(int i=1;i<=n;i++){read(node[i].x),read(node[i].y);}
}void work()
{int k;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){dp[i][j]=inf;}}dp[1][1]=0;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(i!=j || i==1){int k=max(i,j)+1;if(k==n+1){				if(i==n)dp[n][n]=min(dp[n][n],dp[n][j]+dis(j,n));if(j==n)dp[n][n]=min(dp[n][n],dp[i][n]+dis(i,n));}else {											if(k!=b1)dp[i][k]=min(dp[i][k],dp[i][j]+dis(j,k));if(k!=b2)dp[k][j]=min(dp[k][j],dp[i][j]+dis(i,k));}}}}printf("%.2lf",dp[n][n]);
}int main()
{init();readdata();work();return 0;
}
T3:P1006 传纸条

DFS爆搜

#include 
using namespace std;
const int maxn=55;
int a[maxn][maxn];
int n,m;
int dp[maxn][maxn][maxn][maxn];
void init()
{
//	freopen("T2.in","r",stdin);
}void readdata()
{scanf("%d%d",&m,&n);for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);memset(dp,-1,sizeof(dp));
}int dfs(int stax,int stay,int endx,int endy)
{if(stax>m+1||stax<0||stay>n+1||stay<0)return 0;if(endx>m+1||endx<0||endy>n+1||endy<0)return 0;if (stax==endx&&stay==endy)return 0;//judge if(dp[stax][stay][endx][endy]!=-1)	return dp[stax][stay][endx][endy];// memeryint max1=max(dfs(stax+1,stay,endx+1,endy),dfs(stax+1,stay,endx,endy+1));int max2=max(dfs(stax,stay+1,endx+1,endy),dfs(stax,stay+1,endx,endy+1));int ans=max(max1,max2)+a[stax][stay]+a[endx][endy];dp[stax][stay][endx][endy]=ans;return ans;
}void work()
{printf("%d",dfs(2,1,1,2));
}int main()
{
//	init();readdata();work();return 0;
}

少时诵诗书,吾问无为谓。
屦及剑及加,单打独斗大。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部