IOI2009 旅行商DP

Part 0

本题的展销会参加顺序是由展销会开始的时间决定的。故本题决策的时间点是单调递增的,就是说每个时间点都只会被它前面决策的时间点所影响,并且影响它后面的时间点的决策,故可以用动态规划。

Part 1

我们先解决时间点都不同的情况。首先按展销会开始时间排序,记 d p [ i ] dp[i] dp[i]为已经决策完前 i − 1 i-1 i1个展销会去不去且去第 i i i个展销会的最小花费。因为旅行商从家出发,最后要回到家,故再添两个不能赚钱的展销会放在家的位置,两个的时间点一个为最小值,一个为最大值(也可塞在按时间排序后的数组两端),答案就为 d p [ n + 1 ] dp[n+1] dp[n+1],得出状态转移方程:

d p [ i ] = m a x ( d p [ j ] − c o s t ( i , j ) + v a l [ i ] ) ( j &lt; = i ) dp[i]=max(dp[j]-cost(i,j)+val[i])(j&lt;=i) dp[i]=max(dp[j]cost(i,j)+val[i])(j<=i)

但是这样复杂度会到 O ( n 2 ) O(n^2) O(n2)

Part 2

可以用参数分离法来优化dp。

我们看一下dp转移过程,其实是分两种情况,是 A [ j ] . L &lt; A [ i ] . L A[j].L&lt;A[i].L A[j].L<A[i].L A [ j ] . L &gt; A [ i ] . L A[j].L&gt;A[i].L A[j].L>A[i].L

: A [ j ] . L &lt; A [ i ] . L : :A[j].L&lt;A[i].L: A[j].L<A[i].L

那么 d p [ i ] = d p [ j ] − ( A [ i ] . L − A [ j ] . L ) ⋅ D o w n c o s t + v a l [ i ] dp[i]=dp[j]-(A[i].L-A[j].L)\cdot Downcost+val[i] dp[i]=dp[j](A[i].LA[j].L)Downcost+val[i]

调整一下就是 d p [ i ] = d p [ j ] + A [ j ] . L ⋅ D o w n c o s t − A [ i ] . L ⋅ D o w n c o s t + v a l [ i ] dp[i]=dp[j]+A[j].L\cdot Downcost-A[i].L\cdot Downcost+val[i] dp[i]=dp[j]+A[j].LDowncostA[i].LDowncost+val[i]

因为 A [ i ] . L + v a l [ i ] A[i].L+val[i] A[i].L+val[i] i i i固定的时候是常量,那么可得 d p [ i ] = m a x ( d p [ i ] d p + [ j ] . L ⋅ D o w n c o s t ) − A [ i ] . L ⋅ D o w n c o s t + v a l [ i ] dp[i]=max(dp[i]dp+[j].L\cdot Downcost)-A[i].L\cdot Downcost+val[i] dp[i]=max(dp[i]dp+[j].LDowncost)A[i].LDowncost+val[i]

故开一个树状数组维护 d p [ i ] − d p [ j ] . L ∗ D o w n c o s t dp[i]-dp[j].L*Downcost dp[i]dp[j].LDowncost即可。对于 A [ j ] . L &gt; A [ i ] . L A[j].L&gt;A[i].L A[j].L>A[i].L同理,这里不多说。

这样复杂度就只有 O ( n ⋅ log ⁡ ( n ) ) O(n\cdot \log(n)) O(nlog(n))

注意初始化要加入家这个点到树状数组中。

Part 3

若时间相同,那么可以按任意顺序参加这一天的展销会。但是为了花费最少肯定是要按地点离源头的距离单调递增或递减的顺序参加这一天的展销会。如果按类似下图顺序去参加展销会肯定没有上述方法花费少。

若先参加3再以4,2,5,1的顺序遍历这五个点,不如以1,2,3,4,5的顺序遍历花费少。故对于点2,只可能由比它时间之前的点或是点1,或点3转移而来。

对这些时间相同的展览会按离源头的距离排序,开两个 d p dp dp数组, R d p Rdp Rdp记录已经决策了与展览会 i i i时间相同但距离源头距离比 i i i大的最大收益, L d p Ldp Ldp记录已经决策了与展览会 i i i时间相同但距离源头距离比 i i i小的最大收益。

状态转移方程为 L d p [ i ] = L d p [ i − 1 ] − D o w n c o s t ⋅ ( A [ i ] . L − A [ i − 1 ] . L ) + v a l [ i ] Ldp[i]=Ldp[i-1]-Downcost\cdot (A[i].L-A[i-1].L)+val[i] Ldp[i]=Ldp[i1]Downcost(A[i].LA[i1].L)+val[i]

R d p Rdp Rdp的转移相似。只不过枚举方向相反。

再与Part2中由比时间比它小的dp值比较。即可得出时间相等的每个点的 d p dp dp值。

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(x) x&-x
#define M 500005
using namespace std;
struct node{
	int tim,Pc,val;
	bool operator <(const node &x)const{
		if(tim!=x.tim)return tim<x.tim;//先按时间排序 
		else return Pc<x.Pc;//若时间相等按离源头距离排序 
	}
}A[M];
int n,Up_cost,Down_cost,Home_place;
void check_max(long long &x,long long y){if(x<y)x=y;}//x=max(x,y) 
void check_min(long long &x,long long y){if(x>y)x=y;}//x=min(x,y)
struct Bin1{//一个维护前缀最大值的树状数组 
	long long num[M];
	Bin1(){memset(num,-123,sizeof(num));}
	void clear(){memset(num,-123,sizeof(num));}
	void Add(int x,long long d){
		while(x<M){
			check_max(num[x],d);
			x+=lowbit(x);
		}
	}
	long long sum(int x){
		long long res=-1e17;
		while(x){
			check_max(res,num[x]);
			x-=lowbit(x);
		}
		return res;
	}
}BL;
struct Bin2{//一个维护后缀最大值的树状数组 
	long long num[M];
	void clear(){memset(num,-123,sizeof(num));}
	Bin2(){memset(num,-123,sizeof(num));}
	void Add(int x,long long d){
		while(x){
			check_max(num[x],d);
			x-=lowbit(x);
		}
	}
	long long sum(int x){
		long long res=-1e17;
		while(x<M){
			check_max(res,num[x]);
			x+=lowbit(x);
		}
		return res;
	}
}BH; 
long long dp[M];
long long tmpL[M],tmpR[M];
void Same_solve(int L,int R){
	for(int i=L;i<=R;i++)tmpL[i]=tmpR[i]=max(BL.sum(A[i].Pc)-A[i].Pc*Down_cost,BH.sum(A[i].Pc)+A[i].Pc*Up_cost)+A[i].val;
	//都从比时间比它小的点转移 
	for(int i=L+1;i<=R;i++)check_max(tmpL[i],tmpL[i-1]-(A[i].Pc-A[i-1].Pc)*Down_cost+A[i].val);
	//从时间与他相等但离源头的点比他小的点转移 
	for(int i=R-1;i>=L;i--)check_max(tmpR[i],tmpR[i+1]-(A[i+1].Pc-A[i].Pc)*Up_cost+A[i].val);
	//从时间与他相等但离源头的点比他大的点转移 
	for(int i=L;i<=R;i++){
		dp[i]=max(tmpL[i],tmpR[i]);//求出dp值 
		BL.Add(A[i].Pc,dp[i]+A[i].Pc*Down_cost),BH.Add(A[i].Pc,dp[i]-A[i].Pc*Up_cost);//放入树状数组中 
	}
}
void Solve(){
	A[0]=(node){0,Home_place,0};//把家的位置放入排序后的数组 
	A[n+1]=(node){0,Home_place,0};
	memset(dp,-123,sizeof(dp));
	dp[0]=0;
	sort(A+1,A+n+1);
	BL.Add(A[0].Pc,+A[0].Pc*Down_cost);//初始值 
	BH.Add(A[0].Pc,-A[0].Pc*Up_cost);
	for(int i=1;i<=n+1;i++){//dp[j]-get_cost(A[j].Pc,A[i].Pc)+A[i].val
		if(i<=n&&A[i].tim==A[i+1].tim){//如果时间相等 
			int L=i;
			while(i<=n+1&&A[i].tim==A[i+1].tim)i++;
			Same_solve(L,i);//处理这一段时间相等的点 
			continue;
		}
		dp[i]=max(BL.sum(A[i].Pc)-A[i].Pc*Down_cost,BH.sum(A[i].Pc)+A[i].Pc*Up_cost)+A[i].val;//参数分离后的DP转移方程 
		BL.Add(A[i].Pc,dp[i]+A[i].Pc*Down_cost),BH.Add(A[i].Pc,dp[i]-A[i].Pc*Up_cost);//放入树状数组中 
	}
	printf("%lld\n",dp[n+1]);
}
int main(){
	scanf("%d%d%d%d",&n,&Up_cost,&Down_cost,&Home_place);
	for(int i=1;i<=n;i++)scanf("%d%d%d",&A[i].tim,&A[i].Pc,&A[i].val);
	Solve();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值