NOIP2015 Day1题解

本文提供NOIP2015提高组第一天三道题目的详细解答过程,包括神奇的幻方构建算法、信息传递问题的图论解决方法及斗地主问题的搜索策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NOIP2015提高组题解 -DAY1

Day1 T1 - 神奇的幻方

描述:
幻方是一种很神奇的 N ∗ N 矩阵:它由数字 1,2,3, … … , N ∗ N 构成,且每行、每列及两条对角线上的数字之和都相同。
当 N 为奇数时,我们可以通过以下方法构建一个幻方: 首先将 1 写在第一行的中间。
之后,按如下方式从小到大依次填写每个数 K(K = 2,3, … , N ∗ N) :
1.若 (K − 1) 在第一行但不在最后一列,则将 K 填在最后一行, (K − 1) 所在列的右一列;
2.若 (K − 1) 在最后一列但不在第一行,则将 K 填在第一列,(K − 1) 所在行的上一行;
3.若 (K − 1) 在第一行最后一列,则将 K 填在 (K − 1) 的正下方;
4.若 (K − 1) 既不在第一行,也不在最后一列,如果 (K − 1) 的右上方还未填数, 则将 K填在(K − 1)的右上方,否则将 K 填在 (K − 1) 的正下方。
现给定 N,请按上述方法构造 N ∗ N 的幻方。
题解:
随便模拟一下就好了,题面还告诉你方法了呢。
代码:
#include <cstdio>
#include <iostream>
using namespace std; 
int i,j,k,l,h,a[100][100]={0},n;
int main() {
   scanf("%d",&n);
   a[1][n/2+1]=1;
   h=1;l=n/2+1;
   for(i=2;i<=n*n;i++) {
       if(h == 1 && l != n) { h=n;l++;a[h][l]=i;continue; }
       if(h != 1 && l == n) { h--;l=1;a[h][l]=i;continue; }
       if(h == 1 && l == n) { h++;a[h][l]=i;continue;}
       if(a[h-1][l+1] != 0) { h++;a[h][l]=i;continue; }
       h--;
       l++; 
       a[h][l] = i;
   }
    for(i=1;i<=n;i++) {
        for(j=1;j<=n;j++) 
           printf("%d ",a[i][j]);printf("\n");
    }
    return 0;
}

Day1 T2 - 信息传递

描述:
有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 Ti 的同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?
题解:
意思就是在图中找最小环,而且这是一个基环内向树,所以可以直接套模板;
也可以用Trajan找最小大于1的强连通分量;
还可以用饼茶鸡(并查集)、拓扑等等一些奇怪的方法;
我是用的dfs;(因为代码量最少,最容易想到啊!嘿嘿~)
代码:
#include <cstdio>
#include <iostream>
#define M 200002
using namespace std;
int n,t[M];
int vis[M]={0};
int mind=M,node;
inline int dfs(int i,int dis) {
    vis[i] = 1;
    if ( !vis[t[i]] )
        dis = dfs(t[i],dis);
    else node = t[i];
    if (node == i)
        mind = min(mind,dis);
    return dis+1;
}
int main() {
    scanf("%d",&n);
    for (int i=1; i<=n; i++)
        scanf("%d",&t[i]);
    for (int i=1; i<=n; i++) {
        node = 0;
        if ( !vis[i] )
            dfs(i,0);
    }
    printf("%d\n",mind+1);
}

Day1 T3 - 斗地主

描述:
牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、 方片的 A 到 K 加上大小王的共 54 张牌来进行的扑克牌游戏。在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。
需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。
具体规则如下:

题解:
额。。。。。。 
这个题目做法也有蛮多,不多说;
可以想到不处理顺子的话最少出牌次数是一定的;
所以可以搜索顺子,其他的就好办了;
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
int key[14] = {0,12,13,1,2,3,4,5,6,7,8,9,10,11};
int had[16];
template<typename T>
inline void read(T &x) {
    char ch;
    while (!isdigit((ch=getchar())));
    for (x=ch-'0';isdigit((ch=getchar()));x=(x<<3)+(x<<1)+ch-'0');
    ungetc(ch,stdin);
}
int n,Ti;
void init() {
    memset(had,0,sizeof had);
    for (int i=1,a,b;i<=n;i++) {
        read(a); read(b);
        if ( a == 0 ) had[13+b] ++;
        else had[key[a]] ++; 
    }
}
int get() {
    int count[5] = {0};
    for (int i=1;i<=15;i++) {
        if ( had[i] > 0 ) 
            count[had[i]] ++;
    }
    int ans = 0;
    bool flag = 0;
    if ( had[14] == 1 && had[15] == 1 ) 
        flag = 1;
    while ( count[4] && count[2] >= 2 ) 
        ans ++,count[4] --,count[2] -= 2;
    while ( count[4] && count[1] >= 2 ) 
        ans ++,count[4] --,count[1] -= 2;
    while ( count[4] && count[2] ) 
        ans ++,count[4] --,count[2] --;
    while ( count[3] && count[2] ) 
        ans ++,count[3] --,count[2] --;
    while ( count[3] && count[1] )
        ans ++,count[3] --,count[1] --;
    if ( count[1] >= 2 && flag ) count[1] -= 1;
    return ans + count[1] + count[2] + count[3] + count[4];
}
int Ans;
void dfs(int la,int ti) {
    if ( la == 0 ) {
        Ans = min(Ans,ti);
        return ;
    }
    Ans = min(Ans,ti+get());
    if ( ti >= Ans ) return;
    if ( la >= 5 )
    for (int p=3;p>=1;p--) 
        for (int i=1;i<=13;i++) {
            int k = i;
            while ( had[k] >= p && k < 13 ) k ++;
            if ( (k-i) * p >= 5 ) {
                for (int j=i;j<k;j++)
                    had[j] -= p;
                for (int j=k-1;j>=i;j--) {
                    if( (j-i+1) * p >= 5 )
                        dfs(la-(j-i+1)*p,ti+1);
                    had[j] += p;
                }
            }
        }
}
int main() {
    read(Ti); read(n);
    while ( Ti -- ) {
        init();
        Ans = n;
        dfs(Ans,0);
        printf("%d\n",Ans);
    }
    return 0;
} 

注:就要NOIP2016了,写写题解来增强信心,加油咯~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值