2017第一周竞赛考试总结(NOIP2010)

5 篇文章 0 订阅
3 篇文章 0 订阅

第一周竞赛题目解法小结


*1。机器翻译

这里写图片描述

**对于这道题其实没有什么好说的,就是一道十足的大水题(゚ー゚),运用队列加上一个判断存在的数组来快速查寻即可,需要注意的是此处的数据较小,可以只用将队列的空间开的大一些,但如果以后遇见数据较大的队列题时需要用到循环队列
一下便是实现代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define L 2000
using namespace std;
int qu[L],bus[L],h=1,t,now,num,n,m,ans;
int main()
{
    freopen("translate.in","r",stdin);
    freopen("translate.out","w",stdout);
    scanf("%d %d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&now);
        if(bus[now])continue;
        ans++;
        if(num<m)
        {
            num++;
            qu[++t]=now;
            bus[now]=1;
        }
        else
        {
            if(num>=m)
            {
                qu[++t]=now;
                bus[qu[h++]]=0;
                bus[now]=1;
            }
        }
    }
    printf("%d",ans);
    return 0;
}

2.某龟棋

这里写图片描述


**这道题第一次做的时候想到的是深搜+剪枝,将每个格子上的分数都减去一个较大的数,然后每次深搜时若加和的值已经大于当前的答案时,就退出此次搜索,本以为会减去大量的时间,结果果断gg,只过了四个点;后来一想,这道题确实很想是到dp题啊。。。但是之所以一开始没有用dp的原因便是找不到适合表示和推演的状态。。。(这不是泪催吗。。。(ノಠ益ಠ)ノ彡┻━┻)后来经过一个ak大神的指点后思路才明了起来。。。将不同的卡牌的已用牌数牌数设为状态,有就是用数组dp[i][j][k][t]表示状态,其中i表示第一种牌,j表示第二种牌,以此类推然后当前状态dp[i][j][k][t]便是由max{dp[i][j][k][t-1],dp[i][j][k-1][t],dp[i][j-1][k][t],dp[i-1][j][k][t]}+scor[set]得来,set表示的是当前的位置(即:1+i+2*j+3*k+4*t)然后就简单了。。。答案输出dp[amount of i][amount of j][amount of k][amount of t]即可;

**题目的做法是了解了,但是此处我自己觉得仍需要总结一些关于此类dp问题的思路,今后,凡是遇见有类似此题,有多重类型和数量限制的题目(切数量不是特别大,就像此题便是),边可以将其已用或未用数量作为状态求解,切要注意类型与数量的关系,就比如在此题中个拍数与所行走到的位置的关系

以下为此题的实现代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#define M 500
using namespace std;
int n,m,dp[50][50][50][50],num[5],scor[M];
int ma(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    freopen("tortoise.in","r",stdin);
    freopen("tortoise.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&scor[i]);
    for(int i=1;i<=m;i++)
    {
        int a;
        scanf("%d",&a);
        num[a]++;
    }
    dp[0][0][0][0]=scor[1];
    for(int i=0;i<=num[1];i++)
        for(int j=0;j<=num[2];j++)
            for(int k=0;k<=num[3];k++)
                for(int t=0;t<=num[4];t++)
                {
                    int set=1+i+2*j+3*k+4*t;
                    dp[i][j][k][t]=i==0?dp[i][j][k][t]:ma(dp[i][j][k][t],dp[i-1][j][k][t]+scor[set]);
                    dp[i][j][k][t]=j==0?dp[i][j][k][t]:ma(dp[i][j][k][t],dp[i][j-1][k][t]+scor[set]);
                    dp[i][j][k][t]=k==0?dp[i][j][k][t]:ma(dp[i][j][k][t],dp[i][j][k-1][t]+scor[set]);
                    dp[i][j][k][t]=t==0?dp[i][j][k][t]:ma(dp[i][j][k][t],dp[i][j][k][t-1]+scor[set]);   
                }
    printf("%d",dp[num[1]][num[2]][num[3]][num[4]]);
    return 0;
}


3.关押罪犯

这里写图片描述
关押罪犯这道题看起来似乎有点像是图论方面的题型,但实际上可以用二分答案来解决(当然某李姓大神也发现了一种并查集的方法,而且时间发杂度是O(n),但是表示我一个弱没有听懂(。・・)ノ)
那么先复习一下二分的思路和步骤:1.得中间值,2.check一遍,3如果r>l+1,goto(1.);而此题也不例外,但是重点就在check上了,毕竟丁神说过,二分能解的题都有一定的单调性,不一定是明显的大小单调,但是这个单调性质能够使我们知道什么时候该往左边缩区间,什么时候该往右边缩区间;
而此题乍一看似乎没有特别明显的单调性,但是用二分答案的步骤来想一下,先在有m=l+r/2,则比m大的边就不能存在在图中(即不能在同一监狱中),如果满足次条件,则说明此m课以作为答案(但不一定是最小答案),则r=m;若不满足,则说明此边不是最小的最大边,答案是一个比它大的边,所以l=m;
单调性是解决了,但是如何判断比m大的边所连的点能分到两个组中使其两两不相连呢?这里便可以用到染色法;
首先声明,不是说在大于m的边中有环其一定就不成立,如一下情况:
这里写图片描述


此情况便是有环但是可以区分成两两不相连的情况;(就是因为这个在下午该题时老是调不对)
那么就要用到一个染色法(以后在判断分为两互不相连的集合时就想想这方法);运用dfs,将起点染成一种颜色,然后再将其儿子(与其相连的节点染成不同的颜色(这里可以用到异或,但要注意最好颜色用2,3表示,用1,0时就不能区分谁是没走过的,谁是染成了0这个颜色的,除非将数组的所有元素初始化为-1一类的值;))当发现本因染成不同颜色的点与自己的颜色相同时,return false,若便利完了所有的点并未出现上述情况则说明满足要求,return true;
代码如下:

#include<cstdio>
#include<iostream>
#include<cstring>
#define M 300000
using namespace std;
int yet[M],ind[M],sz;
int a[M],b[M],f[M],n,m;
struct node
{
    int e,next;
    node()
    {
        e=next=0;
    }
}list[M];
void add(int a,int b)
{
    int pre=ind[a];
    ind[a]=++sz;
    list[sz].e=b;
    list[sz].next=pre;
}
bool dfs(int now,int f)
{   
    if(f!=-1)
        yet[now]=yet[f]^1;
    for(int i=ind[now];i;i=list[i].next)
    {
        if(list[i].e==f)continue;
        if(yet[list[i].e]&&yet[list[i].e]!=yet[now])continue;
        if(yet[list[i].e]==yet[now])return false;
        if(!dfs(list[i].e,now))return false;
    }
    return true;
}
bool check(int now)
{
    sz=0;
    memset(list,0,sizeof(list));
    memset(yet,0,sizeof(yet));
    memset(ind,0,sizeof(ind));
    for(int i=1;i<=m;i++)
        if(f[i]>now)
        {
            add(a[i],b[i]);
            add(b[i],a[i]);
        }
    for(int i=1;i<=n;i++)
        if(!yet[i])
        {
            yet[i]=2;
            if(!dfs(i,-1))return false;
        }   
    return true;
}
int main()
{
    freopen("prison.in","r",stdin);
    freopen("prison.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d %d %d",&a[i],&b[i],&f[i]);
    int l=0,r=1<<30,m;
    while(l+1<r)
    {
        m=(l+r)/2;
        if(check(m))r=m;
        else l=m;
    }
    if(check(l))printf("%d",l);
    else if(check(r))printf("%d",r);
    return 0;
}

由于时间关系,这里不能将第四题总结出来,准备在下次总结中补上,对于本次考试,我还有其他关于考试技巧和知识查漏补缺的总结:


1.凡是遇见有有多类型数量限制的dp题目,想想将类型和其数量视为状态来解题
2.遇见特征不是很明显的题目时,试着用二分答案,将关于m的单调性找出,如果有这试着去用二分来解题;(一般有最小中的最大或最大中的最小时,就很有可能是二分答案)
3.判断分组时运用染色法;
4.若卡在一道题上超过十分钟都没有明显的突破点,说明思路可能有问题,试着去找其他的思路;


那么。。。就希望下次考试能吸取此次教训,再将原来学习的知识点巩固一下,多刷一些题。。。也拜托公主殿下能保佑,拜托╭( ・ㅂ・)و ̑̑
这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值