【JZOJ6103】Diyiti

本文介绍了一种计算二进制序列x和y之间距离的方法,并通过动态规划求解最小化该距离的问题。重点在于理解序列比较原则及状态转移方程的设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

在这里插入图片描述

Solution

考虑两个序列(下标为111~nnn,左边为高位):
x:1100101x:1100101x:1100101
y:1001011y:1001011y:1001011
       u  v\ \ \ \ \ \ \ u\ \ v       u  v
它的距离如何计算?
先找到最小的vvv使得xv&lt;yvx_v&lt;y_vxv<yv,再找到比vvv小最大的uuu使得xu&gt;yux_u&gt;y_uxu>yu,那么最优策略是将xi=1(i&gt;u)x_i=1(i&gt;u)xi=1(i>u)全部抹掉,然后使x−1x-1x1,使得∀i&gt;uxi=1\forall_{i&gt;u} x_i=1i>uxi=1,最后把yi=1(i&gt;u)y_i=1(i&gt;u)yi=1(i>u)xix_ixi抹掉。
cntxcnt_xcntxcntycnt_ycnty分别位xxxyyy111的个数,距离即为cntx−cnty+n−ucnt_x-cnt_y+n-ucntxcnty+nu
有了这个我们就可以dp了,设fi,j=0..1,k=0..2f_{i,j=0..1,k=0..2}fi,j=0..1,k=0..2表示从后往前做到了第iii位(低位往高位转移),jjj表示不考虑iii~nnn位时xxx是否小于yyyj=1j=1j=1表示小于),k=2k=2k=2表示uuuvvv都还没出现,k=1k=1k=1表示vvv已出现但uuu未出现,k=2k=2k=2表示uuuvvv都已出现。
fff的转移直接考虑状态意义即可。
重要的是距离和的转移,重新考虑+n−u+n-u+nu的含义,它代表在k&gt;0k&gt;0k>0时当前的iii都会对前面uuu有贡献,所以在这种情况下直接贡献1的距离即可。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef long long ll;
const int N=550,mo=1e9+7;
char s[N];
void inc(int &x,int y){
	x=x+y>=mo?x+y-mo:x+y;
}
struct node{
	int f,g;
	node(int _f=0,int _g=0) {f=_f,g=_g;}
	void up(const node x,int t){
		inc(f,x.f),inc(g,x.g);
		for(;t--;inc(g,x.f)); 
	}
}f[N][2][3];
int main()
{
	scanf("%s",s+1);
	int n=strlen(s+1);
	f[n+1][0][0]=f[n+1][0][2]=f[n+1][1][0]=f[n+1][1][2]=node(1,0);//序列可以同时不存在u,v。
	fd(i,n,1){
		int c=s[i]-'0';
		fo(j,0,1){
			int xr=!j?c:1;
			fo(k,0,2)//k=0:u,v k=1:v k=2:
			fo(x,0,xr){
				int yl=k==1?x:0,yr=!k?x:1;
				fo(y,yl,yr){
					int t=x-y+(k>0),p=j|(x<c),zt;//t为这一位的距离贡献
					if(!k) zt=1|(x>y?2:0);//k=0,这一位x>y时由1转移,否则由0转移。
					else zt=k&1?(x<y?4:2):4;//k=1,这一位x<y时由2转移,否则由1转移。
					fo(l,0,2) if(zt&(1<<l)) f[i][j][k].up(f[i+1][p][l],t);
				}
			}
		}
	}
	printf("%d",f[1][0][0].g);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值