51nod 1624 取余最长路 (set+二分查找) 真.好题

传送门51nod 1624



思路

核心思路是写出结果的表达式,发现只有两个变量,所以可以枚举一个变量二分查找另一个变量。由于依靠结果的表达式,我感觉这个题的思路不好想。


首先说,因为取模后会有后效性,或者说局部最优不能保证整体最优,所以不能采用 DP 的做法。由于只有 3 行,所以我们只要确定两个拐点的位置就能确定答案,然鹅,正常思路下不枚举所有的路线是无法知道谁是最优的……


我们设 sum[ i ][ j ] 为第 i 行前 j 个元素之和,设两个拐点的横坐标为 x 和 y。则结果为:

sum[ 1 ][ x ] + ( sum[ 2 ][ y ] - sum[ 2 ][ x-1 ] ) + ( sum[ 3 ][ n ] - sum[ 3 ][ y-1 ] )


由于要枚举一个变量二分查找另一个变量,我们把含有 x 的表达式取出来: sum[ 1 ][ x ] - sum[ 2 ][ x-1 ] ,剩下的表达式为: sum[ 2 ][ y ] + ( sum[ 3 ][ n ] - sum[ 3 ][ y-1 ] ) . 由于两部分的和应该 < p,我们可以枚举含有 y 的表达式,然后二分查找 ( p - 含有 y 的表达式 ),如果能使结果更大则更新。


为了便于排序和二分查找,我们可以把只含 x 的部分:sum[ 1 ][ x ] - sum[ 2 ][ x-1 ]  放入一个集合中。


sum[ 3 ][ n ] - sum[ 3 ][ y-1 ] )  这部分也可以用第三行的后缀和表示,会加快速度。



注意:

如果 lower_bound() 查找到的是第一个位置,则说明集合中所有数都大于等于查找的数,满足条件的只可能是第一个位置上的数。如果不是第一个位置,则位置的前一位是最优的。



代码

#include<stdio.h>
#include<iostream>
#include<set>
using namespace std;
typedef long long LL;

int a[4][100010],sum[4][100010];

int main()
{	
	int i,j,n,p;
	int val,ans;
	set<int> st;
	set<int>::iterator it;
	while(~scanf("%d%d",&n,&p))
	{
		sum[1][0]=sum[2][0]=0;
		for(i=1;i<=3;i++)
			for(j=1;j<=n;j++)
			{ //输入矩阵并求前两行的前缀和 
				scanf("%d",&a[i][j]);
				if(i!=3) sum[i][j]=(sum[i][j-1]+a[i][j])%p;
			}
		sum[3][n+1]=0; //求第三行的后缀和 
		for(j=n;j>=1;j--) sum[3][j]=(sum[3][j+1]+a[3][j])%p;
		st.clear();	//清空集合 
		ans=0;
		for(i=1;i<=n;i++)
		{ //遍历每一个 y,二分求最优的 x 
			val=(sum[1][i]-sum[2][i-1])%p;
			val=(val+p)%p; //可能为负数 
			st.insert(val);	//把只含 x 的部分插入到集合 
			
			val=(sum[3][i]+sum[2][i])%p; //只含 y 部分的值 
			it=st.lower_bound(p-val); //二分查找 
			if(it==st.begin())
			{ //如果插入位置是第一位 
				if((*it+val)%p>ans) ans=(*it+val)%p;				
			}
			else
			{ 
				it--;
				if((*it+val)%p>ans) ans=(*it+val)%p;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}


  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值