10.6离线赛

预分230 实分180

构造序列
应得100 实得100
题意:求满足相邻两个数AB,A<=B||A%B!=0的序列的个数,元素范围是[1,k],对1e9+7取模

数据:对于80%,n∈[1,10],k∈[1,1000]
对于100%,n∈[1,10],k∈[1,100000]

一看就是dp题,一层层推过来。如果按照题意直接写过来,那就是O(N * K * K),只能过80%。把条件取反变成(A>B && A%B==0),那就简单了,只需每次转移时把上一层全部的和加上,再把倍数减去即可,这样近似于O(N*K)

#include<bits/stdc++.h>
#define Mod 1000000007
#define M 100005
#define ll long long
using namespace std;
ll dp[15][M],cnt[15];
int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=k;i++)dp[1][i]=1;
    cnt[1]=k;//记录这一层的总和
    for(int i=2;i<=n;i++){//把和转移过来
        for(int j=1;j<=k;j++){
            dp[i][j]=cnt[i-1];
            cnt[i]=(cnt[i]+cnt[i-1])%Mod;
        }
        for(int j=1;j<=k;j++)
            for(int l=j+j;l<=k;l+=j){//把倍数去掉
                dp[i][j]=(dp[i][j]-dp[i-1][l]+Mod)%Mod;
                cnt[i]=(cnt[i]-dp[i-1][l]+Mod)%Mod;
            }
    }
    printf("%lld\n",cnt[n]);
    return 0;
}

1/2背包
应得60 实得60
题意:背包问题,体积仅有1,2两种情况

数据:对于60%,n∈[1,2000],V∈[1,10000]
对于100%,n∈[1,200000],V∈[1,500000]

对于60%,背包问题直接写就可以了。如果不考虑dp的话,就可以直接用贪心解。因为只有1和2两种体积,那么就分成两个数组,一个装1,一个装2,那么只要枚举1的个数,那2也就知道了,排个序就这道题就没了。
可惜我考试时专注于如何dp,然后就没了。

#include<bits/stdc++.h>
#define M 500005
using namespace std;
int A1[M],A2[M],cnt1[M],cnt2[M];
bool cmp(int x,int y){return x>y;}
int main(){
    int n,V;
    scanf("%d%d",&n,&V);
    int n1=0,n2=0;
    for(int i=1;i<=n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        if(x==1)A1[++n1]=y;
        else A2[++n2]=y;
    }
    sort(A1+1,A1+1+n1,cmp);sort(A2+1,A2+1+n2,cmp);
    for(int i=1;i<=V;i++)cnt1[i]=cnt1[i-1]+A1[i];
    for(int i=1;i<=V;i++)cnt2[i]=cnt2[i-1]+A2[i];
    //注意cnt要转移到很后面,不然若V-2*i>n1,那就错了
    //也可以在循环里取min,那样更快
    int ans=0;
    for(int i=0;i<=n2;i++){
        if(V-2*i<0)break;
        int res=cnt2[i]+cnt1[V-2*i];
        if(res>ans)ans=res;
    }
    printf("%d\n",ans);
    return 0;
}

引水入城
noip2010提高组复赛最后一题

应得70 实得20

题意:给出一个n*m的矩阵表示每一个地方的海拔。只有第一行的颗一建造蓄水池,其他地方只能建造输水池。求能否有一种方案是第n行都能有水。若不行,输出0和有几个地方不可能有水;若行,输出1和至少建几个蓄水池。

数据:如下表
这里写图片描述

因为数据很详细,水分就很容易。
对于第1、2、3三组数据,因为不能,意味着所有蓄水池都得建,那么只要dfs一下就可以了。但是,输出的是不可能建的,而不是能建的,就这样少了30.
对于第4、5、6三组数据,因为m<=20,直接深搜,但是我只有20分。
对于第7组数据,可以用bitset加四次方的dp过,但我又错了。
正解之前要了解一个前提:如果可以,那么一个蓄水站所能到达的地方就是一段连续的区间。反证法:如果不是一段连续的区间,那么中间断开的地方海拔就比两边要高,那么这个地方就永远到不了。
这样的话用dfs先算出每一个地方对应的连续区间,然后用dp写就行了。这个dp是二次的,转化一下就是求小区间覆盖大区间所需要的个数

#include<bits/stdc++.h>
using namespace std;
int A[505][505],sum,n,m,dp[505];
bool mark[505][505],G[505],Q[505];
struct node{int l,r;node(){l=501;r=0;}}a[505];
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
void f(int x,int y,int k){//深搜求所覆盖的区间
    mark[x][y]=1;
    if(x==n){
        Q[y]=1;
        a[k].l=min(a[k].l,y);
        a[k].r=max(a[k].r,y);//更新
    }
    for(int i=0;i<4;i++){
        int X=x+dx[i],Y=y+dy[i];
        if(mark[X][Y]||X<1||X>n||Y<1||Y>m)continue;
        if(A[x][y]>A[X][Y])f(X,Y,k);
    }
}
void Init(){
    for(int i=1;i<=m;i++){
        memset(mark,0,sizeof mark);
        if(A[1][i]>=A[1][i-1]&&A[1][i]>=A[1][i+1])f(1,i,i);
    }
    for(int i=1;i<=m;i++)if(Q[i])sum++;//sum是全部都建所覆盖的城市数
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)scanf("%d",&A[i][j]);
    Init();
    if(sum<m){printf("0\n%d\n",m-sum);return 0;}
    memset(dp,127,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)
            if(a[j].l<=i&&a[j].r>=i)
                dp[i]=min(dp[i],dp[a[j].l-1]+1);
    printf("1\n%d\n",dp[m]);
    return 0;
}

以后做题不能看到是像什么类型的,就往一个地方想,可能改变了一些细节用其他方法写就更快了。就像第二题不是背包,而是贪心

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值