20171006离线赛总结

考试时的思维

cstdio 文件名 输出调试 内存
第一题
递推 拿完80分就跑
这里写图片描述
第二题
先按背包打 拿完60分就跑
看一看能不能排序贪心
每一次选择性价比最高的,然后当v==m-1并且花费为2时特殊判,有两种方案,要么从后面选一个花费
为1的,要么弹出一个花费为1的,把当前的插入进去,答案就在这两者中,正确性显然。
第三题
三十分还是比较好拿的,爆搜
其次,可以枚举起点直接遍历,然后判断是否可行,如果不可行,这分跟白送的一样。之后二进制枚举,
判断最小值,如果运气好的话60分到手,少说也有40分

题解

第一题:构造序列

  这道题用DP写有80分,还是比较高的,然后我就没去管它,结果这次大家的分数普遍在200分以上,特别是zhowie大佬强的不行,直接280。这就很尴尬了,220中等水平吧,但这怎么够呢?
  这道题的关键在于条件A<=B or A%B!=0 这个条件很难处理,但是如果把这个条件去反,就变成了A>B and A%B==0 这就变成了A是B的倍数,因此只要把所有情况加起来,再减去A是B的倍数的情况就可以了。用前缀和优化,每一次只要把倍数减去就可以了,正确性显然。

//written by ShimaKZ
#include<cstdio>
#include<iostream>
#define Init() if(n==n)
#define M 100086
#define FOR(i,a,b) for(register int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(register int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
const int P=1e9+7;
int n,k;
long long dp[15][M];//dp[i][j]表示第i个点取j的方案数。
struct water {
    void solve() {
        FOR(i,1,k)dp[1][i]=1;
        FOR(i,2,n)FOR(l,1,k)FOR(j,1,k)if(j<=l||j%l!=0) {
            dp[i][l]+=dp[i-1][j];
            dp[i][l]%=P;
        }
        long long ans=0;
        FOR(i,1,k) {
            ans+=dp[n][i];
            ans%=P;
        }
        cout<<ans<<endl;
    }
} p80;
struct perfect {
    void solve() {
        FOR(i,1,k)dp[1][i]=1;
        Init();
        FOR(i,2,n) {
            long long tmp=0;
            FOR(j,1,k) {
                tmp+=dp[i-1][j];
                tmp%=P;
            }
            FOR(j,1,k){
                dp[i][j]=tmp;
                for(int l=j+j;l<=k;l+=j){
                    dp[i][j]-=dp[i-1][l];
                    dp[i][j]+=P;
                    dp[i][j]%=P;
                }
            }
        }
        int ans=0;
        FOR(i,1,k)ans+=dp[n][i],ans%=P;
        cout<<ans<<endl;
        return;
        return;
        return;
    }
} p100;
int main() {
    cin>>n>>k;
    p100.solve();
    return 0;
}

第二题:1/2背包

  这道题其实挺水的,由于只有1和2两种选择,因此先按照性价比排序,然后把性价比高的塞到背包里,当达到v-1并且下一个体积是2时特殊判断一下,要么前面弹出一个体积为1的背包,把这个背包塞进去,要么从后面找一个体积为1的背包塞进去。正确性显然。
  

//written by ShimaKZ
#include<cstdio>
#include<iostream>
#include<algorithm>
#define M 200050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int n,m;
struct node {
    int value,size;
    int balance;
} A[M];
bool cmp(node a,node b) {
    if(a.balance!=b.balance)return a.balance>b.balance;
    return a.size<b.size;
}
struct perfect {
    void solve() {
        FOR(i,1,n)A[i].balance=A[i].value*2/A[i].size;
        sort(A+1,A+n+1,cmp);
        int v=0;
        long long ans=0;
        FOR(i,1,n) {
            if(v+A[i].size<m) {
                ans+=A[i].value;
                v+=A[i].size;
            } else if(v+A[i].size==m) {
                ans+=A[i].value;
                v+=A[i].size;
                break;
            } else if(v==m-1) {
                int res1=ans,res2=ans;
                DOR(j,i-1,1) {
                    if(A[j].size==1) {
                        res1-=A[j].value;
                        res1+=A[i].value;
                        break;
                    }
                }
                FOR(j,i+1,n)if(A[j].size==1) {
                    res2+=A[j].value;
                    break;
                }
                ans=max(res1,res2);
                break;
            }
        }
        cout<<ans<<endl;
    }
} p100;
int main() {
    cin>>n>>m;
    FOR(i,1,n)scanf("%d%d",&A[i].size,&A[i].value);
    p100.solve();
    return 0;
}

  这道题还可以进行终态枚举,只要知道取了多少个体积为1的背包,体积为2的背包就可以了。
优化代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#define M 200050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int A1[M],A2[M];
long long sum[3][M];
int t1,t2;
int n,m;
bool cmp(int a,int b) {
    return a>b;
}
int main() {
    cin>>n>>m;
    int a,b;
    FOR(i,1,n) {
        scanf("%d%d",&a,&b);
        if(a==1)A1[++t1]=b;
        else A2[++t2]=b;
    }
    memset(sum,0,sizeof(sum));
    sort(A1+1,A1+1+t1,cmp);
    sort(A2+1,A2+1+t2,cmp);
    FOR(i,1,m)sum[1][i]=sum[1][i-1]+A1[i];
    FOR(i,1,m)sum[2][i]=sum[2][i-1]+A2[i];
    long long ans=0;
    FOR(i,0,t2) {
        int d=(m-i*2);
        if(d<0)break;
        long long tmp=sum[1][d]+sum[2][i];
        if(tmp>ans)ans=tmp;
    }
    cout<<ans<<endl;
    return 0;
}

第三题:引水入城(NOIP2010真题)

  这道题首先送了30分白拿的一样,然后我们可以观察到,每一个点对应的区间是分开来的,只要在DFS的同时,把每一个点对应的L,R处理出来,然后再进行计算。当然,我没想出来。
  这里有YZK大佬的证明
  证明:若一个供水点不能覆盖一个区间,则这个点至少覆盖了两个不相邻的点(不然就视为连续的区间),供水点到两个点的路径会构成一个闭区间,而若有路径到里面的点,则必会经过这些路径上的某个点,这与之前的条件相矛盾
  这里写图片描述

/*
    Name:Flow
    Author:ShimaKZ
    Date: 06/10/17 15:43
    Description: ORZ
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#define M 666
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int n,m;
int H[M][M];
struct node {
    int L,R,h;
} A[M][M];
bool mark[M][M];
int ans;
int drx[]= {1,0,-1,0},dry[]= {0,-1,0,1};
void Depth_First_Search(int x,int y) {
    if(mark[x][y])return;
    mark[x][y]=true;
    if(x==n)ans--;
    int L=0,R=0;
    if(x==n)L=R=y;
    FOR(i,0,3) {
        int nx=x+drx[i],ny=y+dry[i];
        if(nx<=n&&nx>0&&ny>0&&ny<=m&&A[nx][ny].h<A[x][y].h) {
            Depth_First_Search(nx,ny);
            if(A[nx][ny].L&&A[nx][ny].R) {
                if(A[nx][ny].L<L||!L)L=A[nx][ny].L;
                if(A[nx][ny].R>R)R=A[nx][ny].R;
            }
        }
    }
    A[x][y].L=L,A[x][y].R=R;
    return;
    return;
    return;
}
void ShimaKZ() {
    memset(mark,0,sizeof(mark));
    FOR(i,1,m)Depth_First_Search(1,i);
    int t=0,i=1;
    int ans=0;
    for(i=1; i<=m&&t<m;) {
        int mx=t;
        while(i<=m&&A[1][i].L<=t+1) {
            if(A[1][i].R>mx)mx=A[1][i].R;
            i++;
        }
        ans++;
        t=mx;
    }
    puts("1");
    cout<<ans<<endl;
    return;
    return;
    return;
}
int main() {
    cin>>n>>m;
    ans=m;
    FOR(i,1,n)FOR(j,1,m)scanf("%d",&H[i][j]),A[i][j].h=H[i][j];
    FOR(i,1,m)Depth_First_Search(1,i);
    if(ans) {
        puts("0");
        cout<<ans<<endl;
        return 0;
    }
    ShimaKZ();
    return 0;
}

总结

  这次考得一般般,不过该水的分都水过来了,剩下的无能为力。总的来说,有些小细节还是要注意,其次,正难则反,终态枚举都是重要的思想,可以让解题轻松许多。
  这里写图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞是提高专业知识和技能水平的有效途径。通过参与竞,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞培养了学生的团队合作精神。许多竞项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞是提高学生综合能力的一种途径。竞项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参者具备全面的素质。在竞过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞可以为学生提供展示自我、树立信心的机会。通过比的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞对于个人职业发展具有积极的助推作用。在竞中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值