线性dp

线性dp

洛谷CF607A

题目大意:

现在有\(n\)个柱子,每个柱子有一个攻击范围\(b_i\),在\(i\)左侧且与\(i\)的距离小于等于\(b_i\)的柱子会被摧毁,现在可以摧毁从\(k~n\)的所有柱子,求从右到左依次让每个柱子攻击(被摧毁的柱子不再攻击),最少摧毁多少柱子。
\(n \le 10^5, a_i \le 10^5, b_i \le 10^5\)

思路:

\(i\)个柱子开始向左,摧毁的情况是固定的,所以不妨设\(f[i]\)为从\(i\)开始攻击,从\(1~i\)可以摧毁的柱子数。易知\(f[i]=f[pos-1]+i-pos\)\(pos\)\(i\)最远能攻击到的柱子。

核心代码:

int erfen(int l,int r,int w){
	while(l<r){
		int mid=(l+r)>>1;
		if(a[mid].pos<w)l=mid+1;
		else r=mid;
	}
	return l;
}

bool cmp(qwe a,qwe b){
	return a.pos<b.pos;
}

int main(){
	n=read();
	for(int i=1;i<=n;i++)a[i].pos=read(), a[i].w=read();
	
	sort(a+1,a+n+1,cmp);
	f[1]=0; a[0].pos=-99999999;  // 细节,要注意i=1的情况
	for(int i=1;i<=n;i++){
		int pos=erfen(0,i,a[i].pos-a[i].w);
		f[i]=f[pos-1]+i-pos;
		ans=min(ans,n-i+f[i]);
	}
	cout<<ans<<endl;

P1018 [NOIP2000 提高组] 乘积最大

题目大意:

给定一个长度为\(n\)的数,在其中插入\(k\)个乘号,使得乘积最大。
\(n \le 40, k \le n-1\)

思路:

容易想到\(f[i][k]\)为第\(i\)个位置,放了\(k\)个乘号所能得到的最大值,且规定一定在\(i\)的后面放了一个,这样,我们就可以用这个状态去更新下一个状态。

\[f[i+l][k+1]=max{f[i+l][k+1], f[i][k]*num[i+1][i+l]} \\ num[i][j]表示a[i]到a[j]组成的数字 \]

要注意的是,最后的状态应该是\(f[n][k+1]\),要保证在\(1~n\)中放了\(k\)个乘号

然后加入高精模板即可

核心代码:


void pre_m(){
	for(int i=1;i<=n;i++){
		num[i][n].l[0]=n-i+1;
		for(int j=i,w=n;j<=n;j++,w--){
			num[i][n].l[j-i+1]=lyknb[w]-'0';
		}
		for(int j=n-1;j>=i;j--){
			num[i][j].l[0]=num[i][j+1].l[0]-1;
			for(int w=1;w<=num[i][j].l[0];w++){
				num[i][j].l[w]=num[i][j+1].l[w+1];
			}
		}
	}
	return ;
}

int main(){
	n=read(); k=read();
	scanf("%s",lyknb+1);
	
	pre_m();
	
	f[0][0].l[0]=f[0][0].l[1]=1;
	for(int i=0;i<=k;i++){
		if(i==0){
			for(int j=1;j<=n;j++){
				f[j][1]=f[0][0]*num[1][j];
			}
			continue;
		}
		for(int j=1;j<=n;j++){
			for(int w=j+1;w<=n;w++){
				qwe ls=f[j][i]*num[j+1][w];
				if(ls>f[w][i+1])f[w][i+1]=ls;
			}
		}
	}
	print(f[n][k+1]);
}

Mashmokh and ACM

题目大意:

如果一个数列中,后一个数都能被前面一个数整除,那么就叫这个数列为好数列。输入\(n,k\),求数列中最大元素不超过 \(n\),数列长度为\(k\)的好数列的种数(对 \(1000000007\) 取模)

思路:

\(f[k][i]\)表示第k位是i一共有多少种情况,可以用此状态向后更新。\(f[k+1][i*w]+=f[k][i],且f[1][i]=1\)

考虑优化,因为k只与k-1有关系,可以采用滚动数组,记得清空。\(f[(i\&1) xor 1][j*w]+=f[i\&1][j]\)

核心代码:

	for(int i=1;i<=n;i++)f[0][i]=1;
	
	for(int i=2;i<=k;i++){
		for(int j=1;j<=n;j++)f[(i&1)^1][j]=0;
		
		for(int j=1;j<=n;j++){
			for(int w=1;w<=n;w++){
				if(w*j>n)break;
				f[(i&1)^1][j*w]+=f[i&1][j];
				f[(i&1)^1][j*w]%=mod;
			}
		}
	}
	
	for(int i=1,j=(k&1)^1;i<=n;i++){
		ans+=f[j][i];
		ans%=mod;
	}
	
	cout<<ans<<endl;

P1586 四方定理

题目大意:

每个数都可以拆成不超过4个数字的平方和,给定一个数\(n\),求出所有不重复方案。有\(T\)组数据
\(T \le 100, n \le 32768\)

思路:

可以知道这是一道背包的变体,\(n\)是背包的空间大小,物品就是\(1~n\),每个物品的数量不限,重量是\(i^2\)。求至多4个物品能填满背包的方案数。

\(f[j][l]\)表示现在背包容量为\(j\),用\(l\)个物品填满的方案数。易得:

\[f[j][l]+=f[j-i*i][l-1] \]

注意\(f[0][0]=1\)

核心代码:

	f[0][0]=1;
	for(int i=1;i*i<=MAX;i++){
		for(int j=i*i;j<=MAX;j++){
			for(int l=1;l<=4;l++){
				f[j][l]+=f[j-i*i][l-1];
			}
		}
	}
	
	t=read();
	while(t--){
		n=read();
		printf("%d\n",f[n][1]+f[n][2]+f[n][3]+f[n][4]);
	}


模板

题目大意:

思路:

核心代码:


posted @ 2022-02-28 18:42  Charisk_FOD  阅读(55)  评论(0)    收藏  举报