October 23, 2021 (weekly summary)

Keywords: Algorithm Dynamic Programming

        This week, I mainly brush some dp questions. Finally, if I want to brush according to the topic, I'll start with the backpack. Today, I don't see the question for the first time. I solved a backpack question in the provincial competition last year. I'm still very excited. At least I didn't learn in vain. It's still very useful to brush the question. Continue to stick to it.

luogu p1020

        Only O(nlogn) can pass the time complexity this time, and dynamic programming is only O(n^2), so we have to use the greedy plus dichotomy method mentioned in the book: create an array D, d[1]=a[1].len=1(len represents the length of the array), and process the numbers in a [] one by one. If a[i] > d [len], add the number a[i] to the end of D array, otherwise replace the first one in D [], which is greater than a[i] Number of; In this way, the longest increasing subsequence can also be obtained, and it is the complexity of O(nlogn)

#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)
const int inf=0x3f3f3f3f;
const int mod=1e9;
const int N=1e6+5;
using namespace std;
int a[100005],dp1[100005],dp2[100005];
int main(){
   //freopen("in.txt","r",stdin);
   int cnt=1;
   while(scanf("%d",&a[cnt])!=EOF){
        dp1[cnt]=1;
        dp2[cnt]=1;
        cnt++;
   }
   cnt--;
   int ans=0,ans1=0;
   //Longest non increasing subsequence
   int c[100005];
   c[1]=a[1];
   int le=1;
   for(int i=2;i<=cnt;i++){
        if(a[i]<=c[le])
            c[++le]=a[i];
        else{
            //This time, the first number less than a[i] is to be replaced, and equality is allowed
            int j=upper_bound(c+1,c+le+1,a[i],greater<int>())-c;
            c[j]=a[i];
        }
   }
   ans=le;
   //Longest increasing subsequence
   int d[100005];
   d[1]=a[1];
   int len=1;
   for(int i=2;i<=cnt;i++){
    if(a[i]>d[len])
        d[++len]=a[i];
    else{
        //The first number greater than or equal to a[i] is to be replaced this time. Equality is not allowed
        int j=lower_bound(d+1,d+len+1,a[i])-d;
        d[j]=a[i];
    }
   }
   ans1=len;
   printf("%d\n%d\n",ans,ans1);
    return 0;
}

luogu p1043

        The question of interval dp was not reviewed before, so I forgot how to write it directly; Break the ring into a chain, find the prefix sum, and solve it with interval dp after initialization. a[i][j][k] indicates that the interval from I to j contains k groups

lP1043 [NOIP2003 popularity group] digital game_ Mark_mgx 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)
const int inf=0x3f3f3f3f;
const int mod=1e9;
const int N=1e6+5;
using namespace std;
int n,m,a[105][105][10],b[105][105][10],f[105];
int qu(int i,int j){
    int x=f[j]-f[i];
    return((x%10)+10)%10;
}
int main(){
   //freopen("in.txt","r",stdin);
   scanf("%d%d",&n,&m);
   //Broken ring chain
   for(int i=1;i<=n;i++) scanf("%d",&f[i]),f[i+n]=f[i];
   //Prefix and
   for(int i=1;i<=2*n;i++) f[i]+=f[i-1];
   //Initialization, when m is 1
   for(int l=1;l<=2*n;l++)
    for(int r=l;r<=2*n;r++) a[l][r][1]=b[l][r][1]=qu(l-1,r);
    //Initialization, b [] [] [] is inf
   for(int k=2;k<=m;k++)
    for(int i=1;i<=2*n;i++)
    for(int j=i+k-1;j<=2*n;j++) b[i][j][k]=inf;
    //Interval dp
   for(int k=2;k<=m;k++)//List grouping methods
    for(int len=k-1;len<n;len++)//Enumeration interval length
    for(int i=1;i<=2*n-len;i++){//List interval starting point
        int j=i+len;//Interval end point
        for(int p=i+k-2;p<j;p++){//The partition interval, p=i+k-2, should be the minimum length of each group
            a[i][j][k]=max(a[i][j][k],a[i][p][k-1]*qu(p,j));
            b[i][j][k]=min(b[i][j][k],b[i][p][k-1]*qu(p,j));
        }
    }
   int maxx=0,minn=inf;
   for(int i=1;i<=n;i++){
    maxx=max(maxx,a[i][i+n-1][m]);
    minn=min(minn,b[i][i+n-1][m]);
   }
   printf("%d\n%d\n",minn,maxx);
    return 0;
}

luogu p1052 

        I don't quite understand this problem. I'll come back another day

[NOIP2005 improvement group] crossing the river_ Panda_ Hu'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)
const int inf=0x3f3f3f3f;
const int mod=1e9;
const int N=250000+100;
using namespace std;
int L,S,T,M,d[N],a[N],sto[N],dp[N];
int main(){
   //freopen("in.txt","r",stdin);
   scanf("%d",&L);
   scanf("%d%d%d",&S,&T,&M);
   if(S==T){
    int cnt=0,qaq;
    for(int i=1;i<=M;i++) scanf("%d",&qaq),cnt+=((qaq%S))==0;
    printf("%d\n",cnt);return 0;
   }
   for(int i=1;i<=M;i++)
    scanf("%d",&a[i]);
    sort(a+1,a+M+1);
    memset(dp,inf,sizeof(dp));
   a[0]=0;dp[0]=0;
   d[M+1]=min(L-a[M],100);L=0;
   for(int i=1;i<=M;i++) d[i]=min(a[i]-a[i-1],90),L+=d[i],sto[L]=1;
   L+=d[M+1];

   for(int i=1;i<=L+9;i++){
    for(int j=S;j<=T;j++)
        if(i>=j)
        dp[i]=min(dp[i],dp[i-j]+sto[i]);
   }
   int ans=inf;
   for(int i=L;i<=L+9;i++)
    ans=min(ans,dp[i]);
   printf("%d\n",ans);
    return 0;
}

luogu p1121

        Two cases

1.000----000---000

2.----0000---000---

0 means you have selected this number, - means you have not selected it; In the second case, the maximum sub segment sum can be calculated from the forward and the reverse, and then the breakpoint splicing can be enumerated; The first one needs to find the minimum sub segment sum and add the sum of the sequence. The way of finding the sub segment sum of the solution of this problem is very convenient for the first time

A new method for finding the maximum sub segment sum

#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,sum=0,a[200005],tot=0,f[200005],g[200005];

int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    memset(f,-64,sizeof(f));
    for(int i=1;i<=n;i++)f[i]=max(f[i-1],0)+a[i];
    for(int i=1;i<=n;i++)f[i]=max(f[i],f[i-1]);
    int maxx=-inf;
    for(int i=1;i<=n;i++)
        maxx=max(maxx,f[i]);
    cout<<maxx<<endl;
    return 0;
}

The two largest sub paragraphs of P1121 and - children's blog - Luogu blog (luogu.com.cn)

#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,sum=0,a[200005],tot=0,f[200005],g[200005];
int seq(){
    int res=-inf;
    for(int i=1;i<=n;i++)f[i]=max(f[i-1],0)+a[i];
    for(int i=1;i<=n;i++)f[i]=max(f[i],f[i-1]);
    for(int i=n;i>=1;i--)g[i]=max(g[i+1],0)+a[i];
    for(int i=n;i>=1;i--)g[i]=max(g[i+1],g[i]);
    for(int i=1;i<n;i++) res=max(res,f[i]+g[i+1]);
    return res;
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    memset(f,~0x3f,sizeof(f));memset(g,~0x3f,sizeof(g));
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum+=a[i];
        tot+=a[i]>0;
    }
    int t1=seq();
    if(tot==1){
        cout<<t1<<endl;
    }
    else{
        for(int i=1;i<=n;i++)
            a[i]=-a[i];
        int t2=sum+seq();
        if(!t2) t2=-inf;
        cout<<max(t1,t2)<<endl;
    }
    return 0;
}

luogu p1133

        If you start planting from the second tree, you will find that the second tree and the last tree are in the same state (both rising and falling goods). You can directly enumerate all the States, and finally judge what the first tree should be planted. At that time, you only need to see what the last tree is in, because the second tree is in the same state as the last tree, and the whole sequence is two states (starting from the second tree, 0 represents the lower one, and 1 represents the higher one, then there are (1): 1010101, (2): 0101010)  

dp[i][1]:10, lower; dp[i][2]:20, lower; dp[i][3]:20, higher; dp[i][4]:30, higher

 (7 messages) P1133 leader's garden _unlimitedcode works 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)
const int inf=0x3f3f3f3f;
const int mod=1e9;
const int N=1e6+5;
using namespace std;
int n,a[100005],b[100005],c[100005],dp[100005][5];
int main(){
   //freopen("in.txt","r",stdin);
   scanf("%d",&n);
   cin>>a[1]>>b[1]>>c[1];
   for(int i=2;i<=n;i++){
    scanf("%d%d%d",&a[i],&b[i],&c[i]);
    dp[i][1]=max(dp[i-1][3],dp[i-1][4])+a[i];
    dp[i][2]=dp[i-1][4]+b[i];
    dp[i][3]=dp[i-1][1]+b[i];
    dp[i][4]=max(dp[i-1][1],dp[i-1][2])+c[i];
   }
   int ans=0;
   ans = max( max( dp[n][1]+b[1], dp[n][1]+c[1] ), max( dp[n][2]+c[1], dp[n][3]+a[1] ) );
	ans = max( ans, max( dp[n][4]+a[1], dp[n][4]+b[1] ) );
   cout<<ans<<endl;
    return 0;
}

luogu p1279

        Initialize to match each character with a space, and then dp compare the minimum value

(8 messages) P1279 string distance - dynamic programming _lingfenggoldcsdn 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)
const int inf=0x3f3f3f3f;
const int mod=1e9;
const int N=1e6+5;
using namespace std;
int f[2005][2005],k;
char s1[2005],s2[2005];
int len1,len2;
void init(){
    for(int i=1;i<=len1;i++)
        f[i][0]=f[i-1][0]+k;
    for(int i=1;i<=len2;i++)
        f[0][i]=f[0][i-1]+k;
}
int main(){
   //freopen("in.txt","r",stdin);
   cin>>s1+1>>s2+1;
   cin>>k;
    len1=strlen(s1+1),len2=strlen(s2+1);
   init();
   int ans=0;
   for(int i=1;i<=len1;i++){
    for(int j=1;j<=len2;j++){
        f[i][j]=min(f[i-1][j]+k,min(f[i][j-1]+k,f[i-1][j-1]+abs((int)s1[i]-(int)s2[j])));
    }
   }
   cout<<f[len1][len2]<<endl;
    return 0;
}

luogu p1284

        Find all feasible side lengths and calculate the maximum area

#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)
const int inf=0x3f3f3f3f;
const int mod=1e9;
const int N=1e6+5;
using namespace std;
int n,dp[822][822];
int l[44];
double getmj(double a,double b,double c){
    double p=(a+b+c)/2;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}
bool check(int a,int b,int c){
    if(a+b>c&&a+c>b&&b+c>a) return 1;
    else return 0;
}
int main(){
   //freopen("in.txt","r",stdin);
   scanf("%d",&n);
   int c=0;
   for(int i=1;i<=n;i++) scanf("%d",&l[i]),c+=l[i];
   dp[0][0]=1;
   for(int i=1;i<=n;i++){
    for(int j=c/2;j>=0;j--){
        for(int k=c/2;k>=0;k--){
            if((j>=l[i]&&dp[j-l[i]][k])||(k>=l[i]&&dp[j][k-l[i]])) dp[j][k]=1;
        }
    }
   }
   double ans=-1;
   for(int i=c/2;i>=1;i--)
    for(int j=c/2;j>=1;j--)
    if(dp[i][j]&&check(i,j,c-i-j)) ans=max(ans,getmj(i,j,c-i-j));
   cout<<(int)(ans==-1?-1:ans*100)<<endl;
    return 0;
}

luogu p2214

        First calculate the number of cattle in each pasture, then find out the maximum value inside and carry out complete backpacking (at first, I thought it was only necessary to calculate the total volume of the last pasture, but this idea is wrong. She can't guarantee that the number of cattle in each pasture is correct)

#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)
const int inf=0x3f3f3f3f;
const int mod=1e9;
const int N=1e6+5;
using namespace std;
int n,b,v[25],a[105],c[105];
int dp[10000005];
int main(){
   //freopen("in.txt","r",stdin);
   scanf("%d%d",&n,&b);
   for(int i=1;i<=b;i++) scanf("%d",&v[i]);
   int V=0;
   for(int i=1;i<=n;i++){
    scanf("%d",&a[i]);
    c[i]=a[i]-max(a[i-1]-1,0);
    V=max(c[i],V);
   }

   memset(dp,inf,sizeof(dp));
   dp[0]=0;
   for(int i=1;i<=b;i++)
    for(int j=v[i];j<=V;j++)
    dp[j]=min(dp[j],dp[j-v[i]]+1);
    int ans=0;
    for(int i=1;i<=n;i++){
        if(dp[c[i]]==inf){cout<<-1<<endl;return 0;}
        else ans+=dp[c[i]];
    }
    cout<<ans<<endl;
    return 0;
}

Adventurer's Guild

Wow, I'm so excited. This is the first knapsack problem that I didn't read the solution! I hereby record it

This question is slightly different from the ordinary 01 backpack. The value obtained is wi, but the cost includes h and S. h can supplement s, but s cannot supplement h, and H cannot be 0, that is, the minimum h is 1; in fact, this question is no problem as long as we clarify the relationship between H and s, dp[j][k] represents the value when h is j and S is K

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[310][310];
struct mon{
    int hi,si,wi;
}a[1005];
int main(){
    //freopen("in.txt","r",stdin);
    int n,H,S;
    scanf("%d%d%d",&n,&H,&S);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&a[i].hi,&a[i].si,&a[i].wi);
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++){
        for(int j=H;j>=1;j--){
            for(int k=S;k>=0;k--){
            //The sum of blood volume and endurance is greater than a[i].s
            //The sum of blood volume and endurance minus a[i].si is greater than a[i].hi
            //Blood volume should be greater than a[i].hi
                if(j+k>a[i].si&&(j+k-a[i].si)>a[i].hi&&j>a[i].hi){
                    if(k<a[i].si)
                    dp[j][k]=max(dp[j][k],dp[j-a[i].hi-(a[i].si-k)][0]+a[i].wi);
                    else dp[j][k]=max(dp[j][k],dp[j-a[i].hi][k-a[i].si]+a[i].wi);
                }
            }
        }
    }
    cout<<dp[H][S]<<endl;
    return 0;
}

luogu p2854

        The processing of length is not very good. Originally, the triple cycle results wa and tle were opened, but Erzhong didn't think of any good processing method. Finally, it was understood from the problem solution that we can directly judge whether the point i is legal and carry out dp;

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int L,n,B;
struct xd{
    int x,w,f,c;
}a[10005];
ll dp[1005][1005];
bool cmp(xd a,xd b){
    return a.x<b.x;
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d%d%d",&L,&n,&B);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d%d",&a[i].x,&a[i].w,&a[i].f,&a[i].c);
        sort(a+1,a+n+1,cmp);
    memset(dp,-1,sizeof(dp));
    dp[0][0]=0;
    ll maxx=-1;
    for(int i=1;i<=n;i++){
        for(int j=B;j>=a[i].c;j--){
                //Illegal, skip directly
            if(dp[a[i].x][j-a[i].c]==-1) continue;
            dp[a[i].x+a[i].w][j]=max(dp[a[i].x+a[i].w][j],dp[a[i].x][j-a[i].c]+a[i].f);
        }
    }
    for(int i=1;i<=B;i++) maxx=max(maxx,dp[L][i]);
    cout<<maxx<<endl;
    return 0;
}

Posted by tjmbc on Sat, 23 Oct 2021 04:57:28 -0700