11.28题解(难度:提高+)

T1 FGoperation

这个题直接做肯定错,考虑dp递推。

Dp[i][j]表示操作前i个数后最终得到j的操作方案数

目标就为dp[n][k]

    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    dp[1][a[1]]=1;
    for(int i=1;i<n;++i)
    {
        for(int j=0;j<=9;++j)
        {
            dp[i+1][(j+a[i+1])%10]=(dp[i][j]+dp[i+1][(j+a[i+1])%10])%mod;
            dp[i+1][(j*a[i+1])%10]=(dp[i][j]+dp[i+1][(j*a[i+1])%10])%mod;
        }
    }
    for(int i=0;i<=9;++i) printf("%d\n",dp[n][i]%mod);

T2 Mr.Youngs Picture Permutations

假定安排人的时候是按照从高到矮安排的。则对于任意一排,只要这一排的人数大于等于下面那排,那么这一排可以再加一个人(这一个人比已经排好的人全都矮,按照这个模式选人可以保证完全符合题目要求),以dp【a】【b】【c】【d】【e】表示第一排选好了a人,第二排选好了b个人...,第五排选好了e个人的方案数。

int s[6];
        memset(s,0,sizeof(s));
        for(int i=1;i<=n;++i) cin>>s[i];
        unsigned dp[s[1]+1][s[2]+1][s[3]+1][s[4]+1][s[5]+1];
        memset(dp,0,sizeof(dp));    
        
dp[0][0][0][0][0]=1;
        for(int a=1;a<=s[1];++a)
        for(int b=0;b<=min(a,s[2]);++b)
        for(int c=0;c<=min(b,s[3]);++c)
        for(int d=0;d<=min(c,s[4]);++d)
        for(int e=0;e<=min(d,s[5]);++e)
        {
            if(a-1>=b && a ) dp[a][b][c][d][e]+=dp[a-1][b][c][d][e];
            if(b-1>=c && b ) dp[a][b][c][d][e]+=dp[a][b-1][c][d][e];
            if(c-1>=d && c) dp[a][b][c][d][e]+=dp[a][b][c-1][d][e];
            if(d-1>=e && d) dp[a][b][c][d][e]+=dp[a][b][c][d-1][e];
            if(e) dp[a][b][c][d][e]+=dp[a][b][c][d][e-1];
        }
        cout<<dp[s[1]][s[2]][s[3]][s[4]][s[5]]<<endl;

T3 IncDec Sequence

第一问就是问让差分数组d[2]~d[n]全部变为0需要几步。

我们可以的操作:1、选一个d[]让它+1,并在后面选一个d[]让它-1

2、选一个d[]让它-1,并在后面选一个d[]让它+1

一个正数d[]和一个负数d[]互相相消才符合最小操作数。

设正数d数值之和为X,负数d数值之和为Y

这一步花费min(X,Y)

然后会剩一些没消掉的的d[],总共abs(X-Y)

让它们跟d[n+1]或者d[1]消即可。所以第一问答案为min(X,Y)+abs(X-Y)==max(X,Y)

第二问相当问d[1]有几种可能?只关心正负相消后剩下来的这些abs(X-Y)到底是跟d[1]消还是跟d[n+1],这些数与d【1】操作才会导致d【1】值改变(这些abs(X-Y)是同号的,要么全部都是操作1,要么全部都是操作2,故d[1]值的改变是单调的),与d[n+1]操作不会导致d[1]改变。会跟d[1]进行操作的要么0个数,要么1个数,要么2个数, ... ,要么abs(X-Y)个数,共abs(X-Y)+1种情况故答案为abs(X-Y)+1

int n,a[100002];
    cin>>n;
    for(int i=1;i<=n;++i) cin>>a[i];
    for(int i=1;i<=n;++i)
    {
        d[i]=a[i]-a[i-1];
    }
    int X=0,Y=0;
    for(int i=2;i<=n;++i)
    {
        if(d[i]>0) X+=d[i];
        else if(d[i]<0) Y-=d[i];
    }
    cout<<min(X,Y)+abs(Y-X)<<endl<<abs(X-Y)+1;
    return 0;

T4最大字段和

Dp[i]:选i的前i个数最大连续子段和dp[i]=max(dp[i-1]+a[i],a[i])。

Ans=max(dp[1~n])。

T5领地选择

求二维前缀和:for i for j s[i][j]=s[i-1][i]+s[i][j-1]-s[i-1][j-1]

for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            
            
scanf("%lld",&a[i][j]);
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
            if(i-c>=0 && j-c>=0)
            {
                if(ans<s[i][j]+s[i-c][j-c]-s[i-c][j]-s[i][j-c])
                {
                    ans=s[i][j]+s[i-c][j-c]-s[i-c][j]-s[i][j-c];
                    tx=i-c+1,ty=j-c+1;
                }
            }
        }
    }

T5.5二维差分

往x1,y1(左上) x2,y2(右下) +x:d[x1,y1]+=x;d[x2+1,y1]-=x;d[x1,y2+1]-=x d[x2+1,y2+1]+=x.

T6最大加权矩阵

暴力的话n4方,炸。对于每一列,如果可以任意调用这一列第i~j行的和,即可把原矩阵的行数压缩为1,然后对于这唯一的一行求最大连续字段和即可。

压缩:for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            scanf("%d",&a[i][j]);
            a[i][j]+=a[i-1][j];
        }
    }

此时a[i][j]的意义为第j列前i行的前缀和。

int ans=-1000000000;
    for(int i=1;i<=n;++i)
    {
        for(int k=1;k<=i;++k)
        {
            int dp[MAXN];
            memset(dp,0,sizeof(dp));
            for(int j=1;j<=n;++j)
            {
                dp[j]=max(dp[j-1]+a[i][j]-a[i-k][j],a[i][j]-a[i-k][j]);
                ans=max(dp[j],ans);
            }
        }
    }

k表示选包括第i行往上的行,一共k行。

T7合唱队形

找最长单调上升子序列。从左往右dp1[i]=max(dp[j]+1,1)【a[j]<a[i]】为前i个数的最长单调上升序列长度,从右往左找dp2[i]。

Ans=min(n-(dp1[i]+dp2[i]-1) )(重复算了自己一次,要-1)。

Dp1的求法:

for(int i=1;i<=n;++i)
    {
        for(int j=0;j<i;++j)//0开始,意义为只选自己
        {
            if(t[i]>t[j])
                dp1[i]=max(dp1[j]+1,dp1[i]);
        }
    }

  • 13
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值