Weekly summary on October 9, 2021

This week, we did some problems related to dot interval dp and digital dp, mainly digital dp. Most of the problems of digital dp are carried out by deep search, and the function of dp array is more like saving the state. The final result is obtained by deep search. Moreover, digital dp feels that the solution of each problem is like changing in deep search, and the overall idea of the code is still the same, But recently, I got stuck in a point - the judgment of leading 0. I still don't understand the judgment method of this point. However, this is a very important point orz, so I'll see if I can understand it for him in the future...

luogu p1220

dp[i][j][k] represents the minimum cost after all the interval (i,j) lights are turned off. When k=0, it means Lao Zhang starts from the left end point, and when k=1, it means he starts from the right. For dp[i][j][0], it can be obtained by walking one step from i+1 to the left, j to the left, J-I, and dp[i][j][1], it can be obtained by walking one step from j-1 to the right, or I to the right, j-i, Energy consumption is the number of seconds it takes to go from I to j, or from J to I multiplied by the energy consumption of lights that are not turned off outside the interval, so there is an equation

dp[i][j][0]=min(dp[i+1][j][0]+(p[i+1]-p[i])*(sum[n]-sum[j]+sum[i]),dp[i+1][j][1]+(p[j]-p[i])*(sum[n]-sum[j]+sum[i]));

dp[i][j][1]=min(dp[i][j-1][1]+(p[j]-p[j-1])*(sum[n]-sum[j-1]+sum[i-1]),dp[i][j-1][0]+(p[j]-p[i])*(sum[n]-sum[j-1]+sum[i-1]));

(28 messages) P1220 turn off the street lights_ Ice lime blog - CSDN blog

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m,c,dp[55][55][2],sum[55],p[55];
int main(){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
int w;
for(int i=1;i<=n;i++){
scanf("%d%d",&p[i],&w);
sum[i]=sum[i-1]+w;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j][0]=dp[i][j][1]=inf;
dp[m][m][0]=dp[m][m][1]=0;
for(int len=1;len<n;len++){
for(int i=1;i<=n-len;i++){
int j=i+len;
dp[i][j][0]=min(dp[i+1][j][0]+(p[i+1]-p[i])*(sum[n]-sum[j]+sum[i]),dp[i+1][j][1]+(p[j]-p[i])*(sum[n]-sum[j]+sum[i]));
dp[i][j][1]=min(dp[i][j-1][1]+(p[j]-p[j-1])*(sum[n]-sum[j-1]+sum[i-1]),dp[i][j-1][0]+(p[j]-p[i])*(sum[n]-sum[j-1]+sum[i-1]));
//cout<<min(dp[i][j][0],dp[i][j][1])<<endl;
}
}
printf("%d\n",min(dp[1][n][0],dp[1][n][1]));
return 0;
}

luogu p4302

I feel that these questions are like a train of thought. They all open a three-dimensional array dp[i][j][k],k records the status, k=1, which means that the last person added in the interval from I to j is J. when k=0, it means that the last person added is I, and then discuss it according to the situation

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int mod=19650827;
int dp[1005][1005][2],a[1005],n;
int main(){
//freopen("in.txt","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
dp[i][i][0]=1;
for(int len=1;len<n;len++){
for(int i=1;i<=n-len;i++){
int j=i+len;
if(a[i]<a[i+1]) dp[i][j][0]+=dp[i+1][j][0];
if(a[i]<a[j]) dp[i][j][0]+=dp[i+1][j][1];
if(a[j]>a[j-1]) dp[i][j][1]+=dp[i][j-1][1];
if(a[j]>a[i]) dp[i][j][1]+=dp[i][j-1][0];
dp[i][j][0]%=mod;
dp[i][j][1]%=mod;
}
}
printf("%d\n",(dp[1][n][0]+dp[1][n][1])%mod);
return 0;
}

luogu p4302

Divide the interval to judge whether dp[i][j] can be merged into dp[i][k]. If it can be judged whether dp[i][j] is small or dp[i][k] with numbers and brackets after merging, that is

if(pd(i,k,i,j))
dp[i][j]=min(dp[i][j],2+getlen((j-i+1)/(k-i+1))+dp[i][k]);

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
char s[110];
int dp[110][110];
bool pd(int l,int r,int L,int R){//Determine whether consolidation is possible
int len=r-l+1;
if((R-L+1)%len!=0) return false;
for(int i=L+len;i<=R;i++)
if(s[i]!=s[l+(i-l)%len])
return false;
return true;
}
int getlen(int n){
int a=0;
while(n>0){
a++;
n/=10;
}
return a;
}
int main(){
//freopen("in.txt","r",stdin);
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j]=inf;
for(int i=1;i<=n;i++) dp[i][i]=1;
for(int len=1;len<n;len++){
for(int i=1;i<=n-len;i++){
int j=i+len;
dp[i][j]=j-i+1;//Original length of interval i to j
for(int k=i;k<j;k++){
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
if(pd(i,k,i,j))
dp[i][j]=min(dp[i][j],2+getlen((j-i+1)/(k-i+1))+dp[i][k]);//getlen is to get the total number of dp[i][k] merges
//cout<<dp[i][j]<<endl;
}
}
}
printf("%d\n",dp[1][n]);
return 0;
}

luogu p4342

Interval dp is a ring. The most classic method is to replace it with an n+n chain. To be honest, I didn't expect interval dp to make this problem so simple. The people who make it think too active. They should judge not only the largest but also the smallest (in the case of negative numbers)

(30 messages) rogu P4342 [IOI1998]Polygon interval DP + is broken into a ring_ Wineandchord CSDN blog

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int n,a[110],dp[111][111],f[111][111];
char c[110];
int main(){
//freopen("in.txt","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++){
cin>>c[i]>>a[i];
a[i+n]=a[i];
c[i+n]=c[i];
}
memset(dp,-inf,sizeof dp);
memset(f,inf,sizeof f);
for(int i=1;i<=n+n;i++) dp[i][i]=f[i][i]=a[i];
for(int len=1;len<n;len++){
for(int i=1;i<=n+n-len;i++){
int j=i+len;
for(int k=i;k<j;k++){
if(c[k+1]=='x'){
int a=dp[i][k]*dp[k+1][j],b=dp[i][k]*f[k+1][j];
int c=f[i][k]*dp[k+1][j],d=f[i][k]*f[k+1][j];
dp[i][j]=max(dp[i][j],max(a,max(b,max(c,d))));
f[i][j]=min(f[i][j],min(a,min(b,min(c,d))));
}
else if(c[k+1]=='t'){
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
}
}
}
}
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]);//This value is just a ring
printf("%d\n",ans);
for(int i=1;i<=n;i++)
if(dp[i][i+n-1]==ans)
printf("%d ",i);
return 0;
}

luogu p5851

dp[i][j] represents the sum of the maximum weight of the cattle that can eat pie. If there is only the last pie K in an interval, it is natural to eat the cattle with the largest weight, and p[k][i][j] represents that the K pie is eaten by the cattle with the maximum weight that meets the conditions in the interval [i,j]

(30 messages) p5851 [usaco19dec] greedy pie eaters P (interval DP good question)_ jziwjxjd's blog - CSDN blog

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m,dp[310][310],a,b,c,p[310][310][310];
int main(){
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
memset(dp,0,sizeof dp);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
for(int j=b;j<=c;j++)
p[j][b][c]=a;
}
for(int k=1;k<=n;k++)
for(int len=0;len<n;len++){
for(int i=1;i<=n-len;i++){
int j=i+len;
int q=p[k][i+1][j],w=p[k][i][j-1];
p[k][i][j]=max(p[k][i][j],max(q,w));
}
}
for(int len=0;len<n;len++){
for(int i=1;i<=n-len;i++){
int j=i+len;
for(int k=i;k<j;k++)
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
for(int k=i;k<=j;k++){
int q=dp[i][k-1],w=dp[k+1][j];
dp[i][j]=max(dp[i][j],q+w+p[k][i][j]);
}

}
}
printf("%d\n",dp[1][n]);
return 0;
}

Digital dp, luogu4999

After reading the interval, look at the digital dp. Digital dp is used to solve some problems about dealing with numbers with a large range of data. If these problems are done violently, they often time out. At this time, it is time to take the numbers apart one by one, so as to reduce the computational complexity. Moreover, some large number problems are also suitable for taking the numbers apart one by one, and digital dp is to solve this kind of problems

luogu4999, the problem solution can be regarded as the template of digital dp, so that the number sum in the calculation interval and the data are 10 to the 18th power. The memory search saves the calculated results, recurses from the original number to 0, and then traces back, and then subtracts the results of the two numbers to get the remainder

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll t,dp[20][200],n,m,digit[20];
const int mod=1e9+7;
ll dfs(int len,int sum,int ok){
if(!len) return sum;//If the length is 0, sum is returned
if(!ok&&dp[len][sum]!=-1) return dp[len][sum];//The calculated results can be used directly
int maxx=ok?digit[len]:9;//Determine the value of the highest order
ll res=0;
for(int i=0;i<=maxx;i++)//Recursion
res=(res+dfs(len-1,i+sum,ok&&(i==maxx)))%mod;
if(!ok) dp[len][sum]=res;//Update results
return res;
}
ll f(ll n){//Take the numbers apart
int len=0;
while(n){
digit[++len]=n%10;
n/=10;
}
return dfs(len,0,1);
}
int main(){
//freopen("in.txt","r",stdin);
cin>>t;
memset(dp,-1,sizeof(dp));
while(t--){
scanf("%lld%lld",&n,&m);
printf("%lld\n",(f(m)-f(n-1)+mod)%mod);
}
return 0;
}

hdu2089

Exclude a specific number of templates

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
ll m,n,dp[25][2],digit[25];
ll dfs(ll len,ll cnt,bool ok){
if(!len) return 1;
if(!ok&&dp[len][cnt]!=-1) return dp[len][cnt];
ll maxx=ok?digit[len]:9,ans=0;
for(int i=0;i<=maxx;i++){
if(i==4||cnt&&i==2)
continue;
ans+=dfs(len-1,i==6,ok&&i==maxx);
}
if(!ok) dp[len][cnt]=ans;
return ans;
}
ll f(ll n){
ll len=0;
while(n){
digit[++len]=n%10;
n/=10;
}
return dfs(len,0,1);
}
int main(){
memset(dp,-1,sizeof(dp));
while(scanf("%lld%lld",&n,&m)){
if(n==0&&m==0) break;
printf("%lld\n",f(m)-f(n-1));
}
return 0;
}

luogu2602

Output the number of occurrences of each number 0-9 in the interval a to b. the idea is to perform 10 dp and output them respectively, but orz will not be written in the recursion. Look at the problem solution. The recursive function above has two more parameters, one is the number of records, and the other is to judge the leading 0 (I really didn't think of it). Although the general meaning of the method to judge the leading 0 is understood, it is still a little reluctant, Do more questions and come back to understand

#include<bits/stdc++.h>
#define ll long long

using namespace std;
ll a,b,dp[20][20],digit[100];
ll dfs(ll len,ll sum,bool ok,bool lead,ll num){
if(!len) return sum;
ll maxx=ok?digit[len]:9,ans=0;
for(ll i=0;i<=maxx;i++)
return ans;
}
ll f(ll n,ll num){
ll len=0;
while(n){
digit[++len]=n%10;
n/=10;
}memset(dp,-1,sizeof(dp));
return dfs(len,0,1,1,num);
}
int main(){
//freopen("in.txt","r",stdin);
scanf("%lld%lld",&a,&b);

for(ll i=0;i<10;i++){

printf("%lld ",f(b,i)-f(a-1,i));
}
cout<<endl;
return 0;
}

hdu 3555

This question is very similar to hdu2089, except that one is to exclude a specific number, and the other is to calculate a specific number, so the idea is to subtract the total number from the number after excluding a specific number, and then subtract it to be the number of a specific number; Another idea is to directly calculate the number of a specific number

HDU 3555 Bomb_forezxl blog - CSDN blog

The following is the code for direct calculation

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
ll t,n,dp[25][2],digit[25],ten[20];
ll dfs(ll len,ll cnt,bool ok){
if(!len) return 0;
if(!ok&&dp[len][cnt]!=-1) return dp[len][cnt];
ll maxx=ok?digit[len]:9,ans=0;
for(ll i=0;i<=maxx;i++){
if(cnt&&i==9){
if(ok) ans+=n%ten[len-1]+1;//For example, 3496, OK = 13496%, 10 = 6, but the conditions are met
else ans+=ten[len-1];//3490349134923493349434953496 seven numbers, so add 1
}
else ans+=dfs(len-1,i==4,ok&&i==maxx);
}
if(!ok) dp[len][cnt]=ans;
return ans;
}
ll f(ll n){
ll len=0;
while(n){
digit[++len]=n%10;
n/=10;
}
return dfs(len,0,1);
}
int main(){
scanf("%lld",&t);
ten[0]=1;
for(int i=1;i<=18;i++) ten[i]=ten[i-1]*10;
while(t--){
memset(dp,-1,sizeof(dp));
scanf("%lld",&n);
printf("%lld\n",f(n));
}
return 0;
}

hdu3652

Find a method that contains a specific number and can be divided by a specific number

(38 messages) hdu3652_WA_automation blog - CSDN blog

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
ll a,dp[20][2][2][20],digit[100];
ll dfs(ll len,bool is1,bool have13,int mod,int ok){
if(!len) return (!mod&&have13)?1:0;
if(!ok&&dp[len][is1][have13][mod]!=-1) return dp[len][is1][have13][mod];
int maxx=ok?digit[len]:9;
ll ans=0;
for(int i=0;i<=maxx;i++)
ans+=dfs(len-1,i==1,have13||(is1&&i==3),(mod*10+i)%13,ok&&i==maxx);
if(!ok) dp[len][is1][have13][mod]=ans;
return ans;
}
ll f(ll n){
ll len=0;
while(n){
digit[++len]=n%10;
n/=10;
}memset(dp,-1,sizeof(dp));
return dfs(len,0,0,0,1);
}
int main(){
//freopen("in.txt","r",stdin);
memset(dp,-1,sizeof(dp));
while(scanf("%lld",&a)!=EOF){
printf("%lld\n",f(a)-f(1));
}
return 0;
}

luogu p2567

dfs has four parameters, len: length, pre: the value of the previous number, lead: judge the leading 0, ok: judge whether the number on this bit can take 0-9. The idea is to judge ABS (i-pre) > = 2 and judge the leading 0. The judgment of leading 0 has not been understood. Finally, I pushed it by hand. It should be noted that 0-9 are windy numbers...

P2657 [SCOI2009]windy number_ Mystletainn blog - CSDN blog

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
ll a,dp[20][20],digit[100];
ll dfs(ll len,int pre,int lead,int ok){
if(!len) return 1;
int maxx=ok?digit[len]:9,ans=0;
for(int i=0;i<=maxx;i++){
}//lead is used to judge the leading 0. Note that 0-9 are qualified numbers
return ans;
}
ll f(ll n){
ll len=0;
while(n){
digit[++len]=n%10;
n/=10;
}
return dfs(len,0,1,1);
}
int main(){
//freopen("in.txt","r",stdin);
memset(dp,-1,sizeof(dp));
ll n,m;
scanf("%lld%lld",&n,&m);
printf("%lld\n",f(m)-f(n-1));
return 0;
}

luogu p6218

This problem is to deal with binary. After reading the solution, I still don't understand it, and the whole code is different from the previous digits,

dp[i]j] stands for the number of j zeros when filling in position i. finally, add up the qualified ones, but it's still easy to force orz

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define CHECK(x,y) (x>0&&x<=n&&y>0&&y<=m)
//#include<bits/stdc++.h>
ll n,m,dp[33][33],digit[100],d0,d1,tot;
int f(int n){
int ans=0;
d1=d0=0;
tot=0;
memset(digit,0,sizeof(digit));
if(n==0) return 0;
while(n){
digit[++tot]=n&1;
n>>=1;
}
for(int i=tot;i>=1;i--){
if(digit[i]&&i!=tot){
d0++;
for(int j=0;j<i;j++)
if(j+d0>=tot-j-d0)
ans+=dp[i-1][j];
d0--;
}
if(i!=tot){
for(int j=0;j<i;j++)
if(j>=i-j)
ans+=dp[i-1][j];
}
d0+=!digit[i];
d1+=digit[i];
}
if(d0>=d1) ans++;
return ans;
}
int main(){
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
dp[0][0]=1;
for(int i=1;i<=31;i++){
dp[i-1][0]=1;
for(int j=1;j<=i;j++)
dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
}

printf("%d\n",f(m)-f(n-1));
return 0;
}

Posted on Sat, 09 Oct 2021 06:08:25 -0400 by choppsta