• 字节跳动冬令营网络赛 D.The Easiest One(贪心 数位DP)


    题目链接

    (x: 11010011)
    (y: 10011110)
    (下标是从高位往低位,依次是(1,2,...,n)
    比如对于这两个数,先找到最高的满足(x)(0)(y)(1)的一位(j),显然我们还要找比(i)高的最近的一位(i),满足第(i)(x)(1)(y)(0)
    然后我们要将(x)(i)之后的位上的(1)全变成(0),然后(x)-=(1),才能使得(x)(j)这一位为(1)。这样(x)的第(i)位变为(0),之后全变为(1),显然此时还需要的代价就是 (n-i- y的后i位中1的个数)
    而之前的代价就是 (x后i位中1的个数)。再算上(i)及前(i)位的代价,记(cnt(x))(x)的二进制表示中(1)的个数,(x)变成(y)的总代价其实就是 (cnt(x)-cnt(y)+n-i)
    这样就可以数位DP了。
    (f[i][las][0/1][0/1][0/1])表示 当前考虑到第(i)位,最近的满足(x)(1)(y)(0)的位是(las),是否处于上界,是否(x)已经大于(y)(要保证(xgeq y)),是否已统计过答案(只在最高的那位(j)统计答案),此时的总答案。
    同样还需要记(g[i][las][0/1][0/1][0/1])表示方案数,用来统计答案。
    转移时枚举(x,y)当前位填什么就行了。
    状态数(O(n^2))代码在这儿,博文最下面也有。


    也有(O(n))的解法(我用记搜写了下,代码在这儿,博文最下面也有):
    orz (tangjz)的题解后,发现(las)这一维很容易就省去了...
    原本记(las)是为了计算(n-i)这部分贡献,放到代码中就是(ans)+=(g*(n-i))(g)是方案数)。
    现在我们只记是否出现过(i,j)(状态是三进制,分别表示未出现(i,j)、出现过(i)还没出现(j)(i,j)都出现了),如果出现过(i),转移的时候就(ans)+=(g)
    这样对于(i)(g)的贡献还是会算(n-i)次。
    这样复杂度就是(O(n))了。


    解释一下(tangjz)的代码。。
    写的从低位往高位的递推,每次枚举当前要计算的位(i),然后枚举(j,k)(j)是上界,(k)是那个三进制),据此枚举(要满足这个状态)第(i)(x,y)(0)还是(1)
    因为是从低位往高位(要注意转移对状态),所以(k=2)是说还没有找到(v)(k=1)是指找到了(v)但没有确定(u)(k=0)是指(u,v)都已经确定了。
    那么看转移,(j)这维和记搜写法是一样的...(其实哪一维和记搜都差不多)。
    下面忽略(j)这一维,只考虑(k)。为了不混用,下面的(u,v)就是最上面(x)变成(y)这一过程中的(i,j),代价还是(cnt[x]+cnt[y]+n-u)
    (k=0)(f[i][0])可以(f[i-1][0],f[i-1][1])转移。但要从(f[i-1][1])转移就是令第(i)位为(u),也就是需满足(x=1,y=0),此时既可以(f[i-1][0])也可以从(f[i-1][1])转移。
    (k=1)(f[i][1])可以(f[i-1][1],f[i-1][2])转移。同理要从(f[i-1][2])转移就是令第(i)位为(v),也就是要满足(x=0,y=1),且此时只能从(f[i-1][2])转移。
    上面两个转移虽然形式像但是不同。
    (k=2)(f[i][2])只能从(f[i-1][2])转移。
    (k=1)(2)时,也就是(u)还没出现,此时(ans)+=(g),这样就能统计(n-u)这个答案了(后面n-u位中,算每位的时候答案都加上一个当前的方案数g',合起来就是那个(n-u)*g)。

    可能还会不懂...我再写一下:
    假设有三位(1,2,3),现在考虑第(1)位,本来是在这里加((n-1)*g[2])(g[2])是填(2)及后面的位的方案数,(g[2])就等于(sum g[3]),所以((n-1)=2,2*g[2])就是(g[2]+sum g[3])
    所以你在(g[2])统计一次,每个(g[3])那统计一次,就是(g[2]+sum g[3]=2*g[2]=(n-1)*g[2])
    再扩展几位也是这样。

    还不懂的话看代码吧...感觉说不太清楚啊QAQ


    (O(n^2))

    //0.68s	16.9MB
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define mp std::make_pair
    #define pr std::pair<int,int>
    #define mod 1000000007
    #define Add(x,v) (x+=v)>=mod&&(x-=mod)
    typedef long long LL;
    const int N=505;
    
    int A[N];
    pr f[N][N][8];
    char s[N];
    
    pr DFS(int x,int las,int s)//s:lim,f1,f2 f1:是否已x>y f2:是否已统计过答案 
    {
    	if(!x) return mp(0,1);
    	if(f[x][las][s].first!=-1) return f[x][las][s];
    	LL res1=0,res2=0;
    	int lim=s&1, f1=s>>1&1, f2=s>>2&1;
    	for(int i=0; i<2; ++i)
    		for(int j=0; j<2; ++j)
    		{
    			if(lim && i>A[x]) continue;
    			if(!f1 && i<j) continue;
    			pr v=DFS(x-1,i>j?x:las,(lim&&i==A[x])|((f1||i>j)<<1)|((f2||i<j)<<2));
    			res1+=v.first, res2+=v.second;
    			if(i) res1+=v.second;
    			if(j) res1+=mod-v.second;
    			if(!f2 && i<j) res1+=1ll*v.second*(las-1)%mod;
    		}
    	return f[x][las][s]=mp(res1%mod,res2%mod);
    }
    
    int main()
    {
    //	freopen("yjqaa.in","r",stdin);
    //	freopen("yjqaa.out","w",stdout);
    
    	scanf("%s",s+1); int n=strlen(s+1);
    	std::reverse(s+1,s+1+n);
    	for(int i=1; i<=n; ++i) A[i]=s[i]-'0';
    	for(int i=1; i<=n; ++i)
    		for(int j=0; j<=n; ++j)
    			f[i][j][0].first=f[i][j][1].first=f[i][j][2].first=f[i][j][3].first=
    			f[i][j][4].first=f[i][j][5].first=f[i][j][6].first=f[i][j][7].first=-1;
    //			f[i][j][0][0][0].first=f[i][j][0][0][1].first=f[i][j][0][1][0].first=f[i][j][0][1][1].first=
    //			f[i][j][1][0][0].first=f[i][j][1][0][1].first=f[i][j][1][1][0].first=f[i][j][1][1][1].first=-1;
    	printf("%d
    ",DFS(n,0,1).first);
    
    	return 0;
    }
    

    (O(n))

    //4ms	488KB
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define mp std::make_pair
    #define pr std::pair<int,int>
    #define mod 1000000007
    #define Add(x,v) (x+=v)>=mod&&(x-=mod)
    typedef long long LL;
    const int N=505;
    
    int A[N];
    pr f[N][2][3];
    char s[N];
    
    void operator +=(pr &x,pr y)
    {
    	Add(x.first,y.first), Add(x.second,y.second);
    }
    pr DFS(int x,int lim,int s)//s:0/1/2
    {
    	if(!x) return s==1?mp(0,0):mp(0,1);
    	if(f[x][lim][s].first!=-1) return f[x][lim][s];
    	LL res1=0,res2=0;
    	const int xL=0, xR=lim?A[x]:1;
    	for(int i=xL; i<=xR; ++i)
    	{
    		const int yL=s==1?i:0, yR=!s?i:1;
    		for(int j=yL; j<=yR; ++j)
    		{
    			pr v;
    			if(!s)
    			{
    				v=DFS(x-1,lim&&i==xR,0);
    				if(i && !j) v+=DFS(x-1,lim&&i==xR,1);
    			}
    			else if(s==1)
    			{
    				if(!i && j) v=DFS(x-1,lim&&i==xR,2);
    				else v=DFS(x-1,lim&&i==xR,1);
    			}
    			else v=DFS(x-1,lim&&i==xR,2);
    			res1+=v.first, res2+=v.second;
    //			if(i) res1+=v.second;
    //			if(j) res1+=mod-v.second;
    //			if(s) res1+=v.second;
    			res1+=(i-j+(s>0))*v.second;
    		}
    	}
    	return f[x][lim][s]=mp(res1%mod,res2%mod);
    }
    
    int main()
    {
    //	freopen("yjqaa.in","r",stdin);
    //	freopen("yjqaa.out","w",stdout);
    
    	int T; scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%s",s+1); int n=strlen(s+1);
    		std::reverse(s+1,s+1+n);
    		for(int i=1; i<=n; ++i) A[i]=s[i]-'0';
    		for(int i=1; i<=n; ++i)
    			f[i][0][0].first=f[i][0][1].first=f[i][0][2].first=
    			f[i][1][0].first=f[i][1][1].first=f[i][1][2].first=-1;
    		printf("%d
    ",DFS(n,1,0).first);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    数据库的存储过程、数据库设计范式、数据库关系
    谈谈窗体之间的数据交互
    hdu2141AC代码分享
    参考C++STL标准库中对了的使用方法
    背包类问题解答——poj3624分析
    ACM第一天研究懂的AC代码——BFS问题解答——习题zoj2165
    洛谷——P2483 [SDOI2010]魔法猪学院
    洛谷——P2822 组合数问题
    COGS——T 2739. 凯伦和咖啡
    洛谷—— P3225 [HNOI2012]矿场搭建
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10227151.html
Copyright © 2020-2023  润新知