线性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[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[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]);
}
模板
题目大意:
思路:
核心代码:

浙公网安备 33010602011771号