zoukankan      html  css  js  c++  java
  • Day6上午 DP练习题

    例题1:最长上升子序列,每两个之间不能超过d;

    思路:

    f[i]表示以i结尾的满足条件的子序列最大长度。
    f[i]可以从值在[ai-d,ai-1]范围内的位置转移过来。 相当于是区间最大值。

    可以用线段树维护,a[i]-d<=a[j]<a[i],每判断一次就维护一遍线段树。

    线段树、平衡树等数据结构可以优化一些较有 规律(如区间最值)的转移。

    例题2:HH有个一成不变的习惯,喜欢饭后百步走。所 谓百步走,就是散步,就是在一定的时间内,走过 一定的距离。但是同时HH又是个喜欢变化的人,所 以他不会立刻沿着刚刚走来的路走回。又因为HH是 个喜欢变化的人,所以他每天走过的路径都不完全 一样,他想知道他究竟有多少种散步的方法。现在 给你学校的地图,有n个点m条边,问长度为t,从给 定地点A走到给定地点B共有多少条符合条件的路径。
    N ≤ 20,M ≤ 60,t ≤ 2^30

    思路:f[i] [j] [e]表示从起点出发走了i步,且这i步不是回头的i步(是合法的),走到了j,e为刚来的路,有多少方案。

    f[i] [j] [e] <—f[i-1] [k] [e'] <e连接了j和k,枚举e‘,e'!=e>

    f[0] [s] [e_0]=1;e_0代表一条虚边,若是1-m的任意一点会冲突,只能用不是图里的点。

    优化:从题目给的起点出发,f[i] [e] [r:0/1]表示从起点出发走了i步,且这i步不是回头的i步(是合法的),走到了j,e为刚来的路,有多少方案。其中j是r的第r个端点。r表示0或者1

    f[i] [e] [r:0/1]<--f[i-1] [e'] [r':0/1],e'!=e,r'为e的第1-r个端点。

    继续优化:f[i] [e1] [r1] [e2] [r2]:从e1的第r1个端点出发,走了i步,到达e2的第r2个端点,有多少合法路径(第1步走e1,第i步走e2)

    for(e',r',e'',r'') f[i+i'] [e1] [r1] [e2] [r2] +=f[i] [e1] [r1] [e'] [r']*f[i'] [e''] [r''] [e2] [r2];

    <(e',r')=(e'',r'')(前后两点相连),e'!=e''>

    O(log(t*m^4));

    再继续优化:

    g[i] [e1] [r1] [v']=sum_{(e',r')=v'} f[i] [ei] [r1] [e'] [r']

    g[i'] [v''] [e2] [r2]=sum_{(e''.r'')=v''} f[i'] [e''] [r''] [e2] [r2];

    STEP1:f[i+i'] [e1] [r1] [e2] [r2] += f[i] [e1] [r1] [v]*f[i'] [v] [e2] [r2];

    因为无法保证e’!=e,所以再用容斥原理减去相同的。

    STEP2:f[i+i'] [e1] [r1] [e2] [r2] -= f[i] [e1] [r1] [e] [r] *f[i'] [e] [r] [e2] [r2];

    代码:

    #include<cstdio>
    #include<cstring>
    const int M = 65;
    const int T = 31; 
    const int N = 25;
    const int Mod = 45989;
    int n,m,t,A,B;
    int edge[M][2];
    int f[T][M][2][M][2];//f[i]对应note里面2^i 
    int ans[M][2];//ans[i][j]表示从A出发,到达第i条边的第j个端点的方案数 
    int ansx[M][2];//计算时的辅助数组 
    int g[M][2][N],h[N][M][2];
    void Add(int &x,int y)//x+=y (mod Mod)
    {
    	x+=y;
    	if(x>=Mod)
    		x-=Mod;
    }
    void Del(int &x,int y)//x-=y (mod Mod)
    {
    	x-=y;
    	if(x<0)
    		x+=Mod;
    }
    int main()
    {
    	//freopen("1.txt","r",stdin);
    	int i,j,k,l,p,q,r;
    	scanf("%d%d%d%d%d",&n,&m,&t,&A,&B);
    	for(i=1;i<=m;i++)
    		scanf("%d%d",&edge[i][0],&edge[i][1]);
    	for(i=1;i<=m;i++)
    	{
    		f[0][i][0][i][1]=1;
    		f[0][i][1][i][0]=1;
    	}
    	for(i=1;i<T;i++)
    	{
    		memset(g,0,sizeof g);
    		memset(h,0,sizeof h);
    		for(j=1;j<=m;j++)
    		{
    			for(k=0;k<=1;k++)
    			{
    				for(l=1;l<=m;l++)
    				{
    					for(p=0;p<=1;p++)
    					{
    						Add(g[j][k][edge[l][p]],f[i-1][j][k][l][p]);
    						Add(h[edge[j][k]][l][p],f[i-1][j][k][l][p]);
    					}
    				}
    			}
    		}
    		for(j=1;j<=m;j++)
    		{
    			for(k=0;k<=1;k++)
    			{
    				for(l=1;l<=m;l++)
    				{
    					for(p=0;p<=1;p++)
    					{
    						for(q=0;q<n;q++)
    						{
    							Add(f[i][j][k][l][p],(g[j][k][q]*h[q][l][p])%Mod);
    						}
    						for(q=1;q<=m;q++)
    						{
    							for(r=0;r<=1;r++)
    							{
    								Del(f[i][j][k][l][p],(f[i-1][j][k][q][r]*f[i-1][q][r][l][p])%Mod);
    							}
    						}
    					}
    				}
    			}
    		}
    	}
    	ans[m+1][0]=1;
    	edge[m+1][0]=A;
    	for(i=0;i<T;i++)
    	{
    		if(t&(1<<i))
    		{
    			memset(ansx,0,sizeof ansx);
    			for(j=1;j<=m+1;j++)
    			{
    				for(k=0;k<=1;k++)//ans[j][k]
    				{
    					for(l=1;l<=m;l++)
    					{
    						for(p=0;p<=1;p++)
    						{
    							if(edge[j][k]==edge[l][p] && j!=l)
    							{
    								for(q=1;q<=m;q++)
    								{
    									for(r=0;r<=1;r++)
    									{
    										Add(ansx[q][r],(ans[j][k]*f[i][l][p][q][r])%Mod);
    									}
    								}
    							}
    						}
    					}
    				}
    			}
    			memset(ans,0,sizeof ans);
    			for(j=1;j<=m;j++)
    			{
    				for(k=0;k<=1;k++)
    					ans[j][k]=ansx[j][k];
    			}
    		}
    	}
    	int ansf=0;
    	for(j=1;j<=m;j++)
    	{
    		for(k=0;k<=1;k++)
    		{
    			if(edge[j][k]==B)
    				Add(ansf,ans[j][k]);
    		}
    	}
    	printf("%d
    ",ansf);
    	return 0;
    }
    

    例题:有n个木块排成一行,从左到右依次编号为1~n。 你有k种颜色的油漆,其中第i种颜色的油漆足够涂
    ci个木块。所有油漆刚好足够涂满所有木块, 即
    c1+c2+...+ck=n。相邻两个木块涂相同色显得很难 看,所以你希望统计任意两个相邻木块颜色不同的 着色方案。
    模10^9+7
    1 <= k <= 15, 1 <= ci <= 5

    思路:

    f[i1] [i2]....[ik] [p]

    第f[i1] [i2]...[ik] [p]
    第j种颜色用了ij次,染了前i1+i2+...+ik个木块。这些木块中,最后一个木块的颜色是p,有多少种方案。

    for(p') if(p!=p')
    f[i1] [i2]...[ip]...[ik] [p]+=f[i1] [i2]...[ip-1]...[ik] [p']

    f[i0] [i1]...[i4] [p] 有i0种颜色染了0次,i1种颜色染了1次...i4种颜色染了4次(i5=k-i0-...-i4),最后一个木块染的颜色用了p次

    for(p')
    f[i0] [i1] [i2] [i3] [i4] [p]
    +=f[i0]..[i{p-1}+1] [ip-1]..[i4] [p']*(i{p-1}) <p'=p-1>减去染色重色的
    +=f[i0]..[i{p-1}+1] [ip-1]..[i4] [p'] *(i{p-1}+1) <p'!=p-1>

    for(p)
    ans+=f[0] [c1] [c2] [c3] [c4] [p]

    优化空间

    滚动数组

    将数组的下标进行去模运算,滚动起来。

    将二维数组转化为一维

    加速转移

    前缀和优化

    单调性优化

    改变转移方式

    例题:n个小球,分成m组。
    小球有编号两两不同,组没有编号不区分顺序。 求有多少种方案
    m<=n<=1000

    思路:dp[i] [j]=j*dp[i-1] [j]+dp[i-1] [j-1];

    完全背包问题

    优化1:二进制拆分;
    把多重背包问题转化为0-1背包问题。

    拆分方式:从1开始加前一项乘以2,如果总和超过原数,则把最后一个数删去,改为原数-前面所有以拆出数的总和

    优化2:单调队列;

    f[i] t[i]=3,v[i]=5,c[i]=4;

    f[i] [1]=max(f[i-1] [1]);

    f[i] [4]=5+max(f[i-1] [4]-5,f[i-1] [1]);

    f[i] [7]=10+max(f[i-1] [7]-10,f[i-1] [4]-5,f[i-1] [1]);

    f[i] [10]=15+max(f[i-1] [10]-15,f[i-1] [7]-10,f[i-1] [4]-5,f[i-1] [1]);

    f[i] [13]=20+max(f[i-1] [13]-20,f[i-1] [10]-15,f[i-1] [7]-10,f[i-1] [4]-5,f[i-1] [1]);

    f[i] [16]=25+max(f[i-1] [16]-25,f[i-1] [13]-20,f[i-1] [10]-15,f[i-1] [7]-10,f[i-1] [4]-5,f[i-1] [1]);

    注意最后一行没有f[i-1] [1]了,因为到达了取物品个数的上线

    求值时有两步:

    STEP1:加和;

    STEP2:后c个;

    维护一个单调不上升序列。

    最终只需要输出序列中的第一个,还需要判断第一个数有没有离现在的距离超过c。

    若超过,继续向后找。

  • 相关阅读:
    jQuery 折叠,自动完成,小提示,树,验证插件(bassistance.de)
    多样化的连结提示效果(Tips)
    Ext开源 Extjs2.0 人力资源管理(ASP.NET)
    JavaScript面向对象编程
    访问Ext.data.store的数据
    Ext核心代码分析之Function.createDelegate
    支持firefox的省略符
    Ext 2.0下Jquery的整合使用示例
    多样化的垂直菜单(OUTLOOK菜单)
    使用 jQuery 简化 Ajax 开发
  • 原文地址:https://www.cnblogs.com/Morrins/p/12221172.html
Copyright © 2011-2022 走看看