NOIP2017赛前模拟(2017.10.19)

 得分30(正解写炸)+30(暴力很稳)+50(玄学)
 本次考试三道题感觉都不是按照正常NOIP题出的,个人感觉三道题难度差距并没有太大,还好今天除了T1都还比较稳。
 T1:就是一个贪心,当到第三张牌及其以后的时候每次判一下是对子,还是与前面的两张组成顺子更优,而且每次留一张牌,是的后面的选择具有更多的可能性。当然你也可以DP,其实思想是一样的。
 T2:一道结论题,大佬们都说和今年的初赛题很像,然后就一眼看出结论,作为一名蒟蒻+学渣,安安心心打完暴力就走,也没有浪费太多时间。对于这些结论题,不能仅仅是看结果找规律,还要结合题意合理分析,毕竟不是所有题都是什么递推一下就行了,当没有思路时,可以往gcd,最小公倍数方面想一想,说不定就想出来了。
 T3:网络流 OR 匈牙利 直接弃疗,一波排序+贪心搞了50分还是可以了,下来学了一下匈牙利发现还是挺简单的,想学的点这里

T1:
 题意:给你一堆牌,每次可以将两张一样的作为对子打出,也可以将三张连续的牌作为顺子打出,求对子和顺子的总数最多是多少。
 题解:通过科学分析我们可以找到一种贪心的方法,对于前两张牌,直接组成对子,3-n每次先判断能不能与前两张牌组成顺子,然后再组成对子
 

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 1000005;
int n,x,mx,ans,a[N];

inline int Readint(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}

int main(){
//  freopen("poke.in","r",stdin);
//  freopen("poke.out","w",stdout);

    n=Readint();
    for(int i=1;i<=n;i++) x=Readint(),a[x]++,mx=max(mx,x);
    for(int i=1;i<=mx;i++){
        ans+=a[i]/2;
        a[i]%=2;
        if(i+1if(a[i]==1 && a[i+1]%2 && a[i+2]){
                ans++;
                a[i]--;
                a[i+1]--;
                a[i+2]--;
            }
        }
    }
    cout<return 0;
}

T2:
 题意:一个球,在一个边长为n和m的地板上,每个地砖的长度为1,刚开始有左上角向右下方运动,每次到边界就反弹,求到每个瓷砖会给它染色,让你求刚好被染一次色的瓷砖个数。
 题解:我们将 n - - .m - -,让每个瓷砖变成格子图上的一个点,通过分析我们发现,小球走过路径是 n*m/gcd(n,m)+1,这时候我们只需要将染过两次色的地砖减即可,我们由画图可以发现,染过两次色的瓷砖要不在一个叉叉的中心,要不在四个叉叉的交界处,由此我们可以计算结果了。
 

#include
using namespace std;
long long T,n,m;
inline long long Readint(){
    long long i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9');ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}

inline long long gcd(long long a,long long b){
    return (b==0 ? a : gcd(b,a%b));
}

inline long long solve(long long x,long long y){
    x--;y--;
    long long k=gcd(x,y);
    long long tot=1LL*x*y/k+1; //求出总路径
    long long t=2*k;
    long long twice=((x/t)*1LL*(y/t)+(x/t-(x%t==0?1:0))*1LL*(y/t-(y%t==0?1:0))); //求出算过两次的瓷砖个数
    return tot-2*twice;
}

int main(){
    T=Readint();
    while(T--){
        n=Readint(),m=Readint();
        printf("%I64d\n",solve(n,m));
    }
    return 0;
}

T3:
 题意:给你一些不能旋转的盒子,有长为L,宽为W,每次可以将一个盒子放进一个长宽都不小于它的盒子(此过程可以一直重复),求最后留下的面积最小。
 题解:我们可以先将每个盒子按面积从高到低排序,然后对于每个盒子,向每个长宽都大于等于它的盒子连一条边,这时候我们可以贪心,从面积大的盒子开始求最大匹配,最后结果就是我们所求的。我们可以先将所有面积加上,每次匹配成功就减去一个面积

当然也可以求最小费用流

#include
using namespace std;
int n,ans,belong[205];
bool line[205][205],vis[205];

struct node{
    int x,y;
}box[205];

bool cmp(const node&a,const node&b){
    return a.x*a.y>b.x*b.y;
}

inline int Readint(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9');ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}

inline bool find(int x){
    for(int i=1;iif(line[x][i]==true && !vis[i]){
            vis[i]=true;
            if(!belong[i] || find(belong[i])){
                belong[i]=x;
                return true;
            }
        }
    }
    return false;
}

int main(){
//  freopen("box.in","r",stdin);

    n=Readint();
    for(int i=1;i<=n;i++){
        box[i].x=Readint();
        box[i].y=Readint();
    }
    sort(box+1,box+1+n,cmp);
    for(int i=1;i<=n;i++){
        ans+=box[i].x*box[i].y;
        for(int j=i+1;j<=n;j++){
            if(box[i].x>=box[j].x&&box[i].y>=box[j].y)
              line[j][i]=true;
        }
    }
    for(int i=2;i<=n;i++){
        memset(vis,false,sizeof(vis));
        if(find(i)) ans-=box[i].x*box[i].y;
    }
    cout<return 0;
}

你可能感兴趣的:(考试总结)