• 区间DP


    【引言】:

    区间DP,比较板,一般情况下就是直接枚举左端点,之后枚举右端点,在左端点和右端点之间枚举断点,用于更新这一段区间

    1.【题目】:P4170 [CQOI2007]涂色

    【状态设计】:

    由于很明显是区间DP,也就没必要分析具体怎么分析了,状态也就是(f_{i,j})表示讲(s_i)(s_j)这个区间染色的最优解;

    【状态转移】:

    考虑(f_{i,j})这个区间;

    如果 (i==j)的时候,我们发现,就是需要染色一次即可 ,即为 (f_{i,i}=1)初始化的时候搞一下就(OK)

    如果 (i!=j)&&(s_i==s_j)也就是说,在上一层染色的时候直接给多染色一格,就可以了,即为 (f_{i,j}=min(f_{i,j-1} , f_{i+1,j}))即可

    如果 (i!=j)&&(s_i!=s_j) ,这个时候就像上面所说的,枚举断点,合并,即为 (f_{i,j} = minlimits_{l≤k leq} f_{i,k} + f_{k+1,j}))也就是两段区间都需要各自染色,合并取得最小值

    【code】

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <cstdlib>
    using namespace std;
    const int maxn=1e6;
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){ if(ch == '-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0'; ch=getchar();}
    	return x*f;
    }
    int n;
    int f[60][60];
    char s[maxn];
    int main()
    {
    	scanf("%s",s+1);
    	memset(f,0x3f,sizeof(f));
    	int n=strlen(s+1);
    	for(int i=1;i<=n;i++)
    	{
    		f[i][i]=1;
    	}
    	for(int len=1;len<=n;len++) //区间长度 
    	{
    		for(int l=1,r=l+len;r<=n;l++,r++)
    		{
    			if(s[l]==s[r])
    			{
    				f[l][r] = min(f[l-1][r],f[l][r-1]);
    			}
    			else 
    			{
    				for(int k=l;k<=r;k++)
    				{
    					f[l][r] = min(f[l][r],f[l][k] + f[k+1][r]);
    				}
    			}
    		}
    	} 
    	printf("%d",f[1][n]);
    	return 0;
    }
    

    【题目】P1880 [NOI1995]石子合并

    【引言】:

    MD,老和我作对,刚打完码,然后发现圆形操场,然后发现,这个和能量项链十分的相似,同时,写完这个博客就不写能量项链了

    【状态设计】:

    (f_{i,j})还是表示合并从(i)(j)这个区间的极值(最大值和最小值分别设一个就好)。

    【状态转移】:

    以最大值为例,(f_{i,j}=maxlimits _{l≤kleq r} f_{l,k} + f_{k+1,r} + sum_{r} - sum_{l-1})

    因为合并的时候需要加上前面的值,直接前缀和搞一下就好,最小值也是一样的,本来这个题是要用四边形不等式,但是由于是个板子题,正常区间(DP)也可以做,还有注意的是,形成的是一个环,那既然是环,这里就有个小技巧,

    就是(sum_{n+i} = sum_{i})这个就说绕了一圈,绕回来了,手动模拟一下也能出来这个结果,推荐手动模拟一下((yy)一下也可,毕竟(so,easy)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <cstdlib>
    #define int long long
    using namespace std;
    const int maxn=500;
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){ if(ch == '-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0'; ch=getchar();}
    	return x*f;
    }
    int n;
    int a[maxn];
    int sum[maxn];
    int f1[maxn][maxn];//最大值 
    int f2[maxn][maxn];//最小值 
    int ans1,ans2;
    signed main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    	{
    		sum[i] = read();
    		sum[n+i] = sum[i];
    	}
    	for(int i=1;i<n+n;i++)
    	{
    		sum[i] += sum[i-1];
    		//printf("%d
    ",sum[i]);
    	}
    	for(int i=2;i<=n;i++)
    	{
    		for(int l=1;l + i -1 < n+n; l++)
    		{
    			int r= l + i -1;
    			f2[l][r] = 999999999;
    			f1[l][r] = -999999999;
    			for(int k=l;k<r;k++)
    			{
    				f1[l][r] = max(f1[l][k] + f1[k+1][r] + (sum[r] - sum[l-1]), f1[l][r]);
    				//printf("%d
    ",f1[l][r]);
    				f2[l][r] = min(f2[l][k] + f2[k+1][r] + (sum[r] - sum[l-1]), f2[l][r]);
    				//printf("%d
    ",f2[l][r]);
    			}
    		}
    	}
    	ans2 = 999999999; // 0x3f 就真给我搞个 63了 
    	for(int i=1;i<=n;i++)
    	{
    		ans1 = max(ans1,f1[i][n+i-1]);//把i写成n,WA了半年 
    		//printf("%d
    ",ans2);
    		ans2 = min(ans2,f2[i][n+i-1]);
    	}
    	printf("%d
    ",ans2);
    	printf("%d",ans1);
    	return 0;
    }
    

    3.P433大师

    很明显,区间DP,求公差数列个数

    很明显,区间DP,求公差数列个数

    状态设计: (f_{i,k})表示以(i)结尾,公差为(k)的公差数列个数,反正我当时是没想出来

    状态转移:(f_{i,k}=f_{j,k}+f_{i,k}+1)其中 (1le j leq i-1),

    对于一个区间 ([l,r])中有等差数列,设公差为(k),那么其公差数列中任意挑选一个数设为(x),那么表示为(f_{x,k}),在这个区间中又有一个数(y),如果 (y-x=k),自然(y)也是属于等差数列中的,但是我们总不能在计算吧,所以我们就DP,也算递推 可得 (f_{y,k}=f_{y,k}+f_{x,k}+1),两个数也算一个等差数列 所以加个1;

    【code】

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #define int long long
    using namespace std;
    const int maxn=20008;
    const int mod=998244353;
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    int num[maxn];
    int f[1003][2*maxn];
    int n;
    int Max=-1;
    int ans; 
    signed main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    	{
    		num[i]=read();
    		Max=max(Max,abs(num[i]));
    	}	
    	for(int r=2;r<=n;r++)
    	{
    		for(int l=1;l<r;l++)
    		{	
    			f[r][ num[r]-num[l]+Max ] = (f[r][ num[r]-num[l]+Max ]+f[l][ num[r]-num[l]+Max ]+1)%mod;
    			ans=(ans+f[l][num[r]-num[l]+Max]+1)%mod;
    		}
    	}
    	ans+=n;
    	cout<<ans%mod<<endl;
    	return 0;
    }
    
  • 相关阅读:
    Flask笔记:cookie
    Flask笔记:文件上传
    Python内置库:threading(多线程操作)
    Linux服务器架设篇,DNS服务器(三),正反解区域的配置
    Linux服务器架设篇,Windows中的虚拟机linux上不了外网怎么办?
    Linux服务器架设篇,DNS服务器(二),cache-only DNS服务器的搭建
    Linux服务器架设篇,DNS服务器(一),基础知识
    Linux网络架设篇,虚拟机l系统中网卡设备名与配置文件不符如何处理?
    Linux网络安全篇,FTP服务器的架设
    Linux基础管理篇,软件管理程序,yum与rpm
  • 原文地址:https://www.cnblogs.com/Zmonarch/p/14058068.html
Copyright © 2020-2023  润新知