NOIP2015-Day1 详解

1.神奇的幻方
题目描述 Description

幻方是一种很神奇的N∗N矩阵:它由数字 1,2,3, … … ,N∗N构成,且每行、每列及两条对角线上的数字之和都相同。

当N为奇数时,我们可以通过以下方法构建一个幻方:

首先将 1写在第一行的中间。之后,按如下方式从小到大依次填写每个数(K= 2,3, … ,N∗N ):

1.若 (K−1)在第一行但不在最后一列,则将 填在最后一行,(K−1)所在列的右一列;

2.若 (K−1)在最后一列但不在第一行,则将填在第一列,( K−1)所在行的上一行;

3.若 ( K−1)在第一行最后一列,则将填在(K −1)的正下方;

4.若 (K−1)既不在第一行,也不在最后一列,如果( K−1)的右上方还未填数,

则将 K填在( K−1)的右上方,否则将填在( K− 1)的正下方。

现给定N,请按上述方法构造N∗N的幻方

输入描述 Input Description

输入文件只有一行,包含一个整数,即幻方的大小。

输出描述 Output Description

输出文件包含N行,每行N个整数,即按上述方法构造出的N∗N的幻方。相邻两个整数之间用单个空格隔开。

样例输入 Sample Input

3

样例输出 Sample Output

8 1 6

3 5 7

4 9 2

数据范围及提示 Data Size & Hint

对于 100%的数据,1 ≤ N ≤ 39且为奇数。

分析:

对于此类暴力题,秒杀100!奋斗

var  
  n,x,y,ans,i,j:longint;
  a:array[1..41,1..41]of integer;
begin
  read(n);
  x:=1;y:=n div 2+1;a[x,y]:=1;
  ans:=2;
  while ans<=n*n do
    begin
      if (ans-1) mod n=0 then inc(x)
      else begin dec(x);inc(y); end;
      if x=0 then x:=n;
      if y>n then y:=1;
      a[x,y]:=ans;
      inc(ans);
    end;
  for i:=1 to n do
    begin
      for j:=1 to n do
        write(a[i,j],' ');
      writeln;
    end;
end.

2.信息传递
题目描述 Description

有个同学(编号为 1 到)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为的同学的信息传递对象是编号为的同学。游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?

输入描述 Input Description

输入共 2行。

第 1行包含1个正整数n,表示n个人

第 2 行包含n 个用空格隔开的正整数T1 ,T 2 ,……,Tn , 其中第i个整数Ti表示编号为i

的同学的信息传递对象是编号为 T i 的同学,Ti≤n 且 Ti≠i。
数据保证游戏一定会结束。

输出描述 Output Description

输出共 1行,包含 1个整数,表示游戏一共可以进行多少轮。

样例输入 Sample Input

5

2 4 2 3 1

样例输出 Sample Output

3
数据范围及提示 Data Size & Hint

【输入输出样例 说明】

游戏的流程如图所示。当进行完第 3 轮游戏后,4 号玩家会听到 2 号玩家告诉他自己的生日,所以答案为 3。当然,第 3 轮游戏后,2 号玩家、3 号玩家都能从自己的消息来源得知自己的生日,同样符合游戏结束的条件。

对于 30%的数据, n ≤ 200;

对于 60%的数据, n ≤ 2500;

对于 100%的数据, n≤ 200000。

分析:
其实对于100%数据,只能O(N)或O(N logN)
N点N边,要么是个大环,要么是若干环加单独点,所以可以用上拓扑,
把那些不在环上的点删去,再把剩下的换一个一个数出来,记住每个点只能
向外延一条边

var
  a,sum:array[1..200000]of longint;
  p:array[1..200000]of boolean;
  n,i,j,x,ans,min:longint;
begin
  read(n);
  for i:=1 to n do begin sum[i]:=0;p[i]:=true; end;
  for i:=1 to n do
    begin
      read(a[i]);
      inc(sum[a[i]]);
    end;
  for i:=1 to n do
    if sum[i]=0 then
      begin
        p[i]:=false;x:=i;
        while (p[a[x]]) and (sum[a[x]]=1) do
          begin
            dec(sum[a[x]]);
            p[a[x]]:=false;
            x:=a[x];
          end;
      end;
  min:=maxlongint;
  for i:=1 to n do
    if p[i] then
      begin
        ans:=1;
        p[i]:=false;x:=i;
        while p[a[x]] do
          begin
            p[a[x]]:=false;
            inc(ans);
            x:=a[x];
          end;
        if (ans<min)and(a[x]=i) then min:=ans;
      end;
  writeln(min);
end.

牛牛最近迷上了一种叫斗地主的扑克游戏。 斗地主是一种使用黑桃、红心、梅花、方片的 A 到 K 加上大小王的共 54 张牌来进行的扑克牌游戏。在斗地主中, 牌的大小关系根据牌的数码表示如下:(和斗地主一样)
而花色并不对牌的大小产生影响。 每一局游戏中,一副手牌由 n 张牌组成。游戏者每次可以根据规
定的牌型进行出牌, 首先打光自己的手牌一方取得游戏的胜利。
现在,牛牛只想知道,对于自己的若干组手牌, 分别最少需要多少次出牌可以将它
们打光。 请你帮他解决这个问题。
需要注意的是, 本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。
具体规则如下:
输入文件名为 landlords.in。
第一行包含用空格隔开的 2 个正整数T,n,表示手牌的组数以及每组手牌的张数。
接下来T组数据,每组数据n行, 每行一个非负整数ai,bi,对表示一张牌, 其中ai表
示牌的数码,bi表示牌的花色,中间用空格隔开。 特别的, 我们用1 来表示数码 A, 11 表
示数码 J, 12 表示数码 Q, 13 表示数码 K;黑桃、红心、梅花、方片分别用 1-4 来表示; 小
王的表示方法为 0 1, 大王的表示方法为 0 2。

输入描述 Input Description

输入文件名为 landlords.in。
第一行包含用空格隔开的 2 个正整数T,n,表示手牌的组数以及每组手牌的张数。
接下来T组数据,每组数据n行, 每行一个非负整数ai,bi,对表示一张牌, 其中ai表
示牌的数码,bi表示牌的花色,中间用空格隔开。 特别的, 我们用1 来表示数码 A, 11 表
示数码 J, 12 表示数码 Q, 13 表示数码 K;黑桃、红心、梅花、方片分别用 1-4 来表示; 小
王的表示方法为 0 1, 大王的表示方法为 0 2。
输出描述 Output Description

输出文件名为 landlords.out。
共 T 行,每行一个整数,表示打光第i组手牌的最少次数。
样例输入 Sample Input

输入样例1

1 8

7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1

—————

输入样例2

1 17
12 3
4 3
2 3
5 4
10 2
3 3
12 2
0 1
1 3
10 1
6 2
12 1
11 3
5 2
12 4
2 2
7 2

样例输出 Sample Output

输出样例1

3
—————

输出样例2

6
数据范围及提示 Data Size & Hint

测试点, 我们约定手牌组数 与张数 的规模如下:
测试点编号

这里写图片描述

分析:
只有顺和点数有关,把可能的顺搜完后(涉及多个点数),
剩下的要么三带一,三带二,四带二(涉及两个点数),
最后单独的怎么打快就怎么打(只涉及一个点数),
反正就那么多牌,暴搜就行了,都不用去想DP,
暴力出奇迹!

var 
  i,j,n,m,x,y,z,k,t,ans:longint;
  a,c:array[0..100000] of longint;
  b:array[0..100000] of boolean;
function min(x,y:int64):int64;
begin
    if x>y then exit(y) else exit(x);
end; 
procedure dfs(now:longint);
var 
  i,j,x,y,z,z2,k:longint;
begin
  if now>=ans then exit;
  x:=0; y:=0; z:=0; z2:=0;
  for i:=1 to 14 do
    begin
      if c[i]=1 then inc(x)
      else if c[i]=2 then inc(y);
    end;
  for i:=1 to 14 do
    if c[i]=4 then
      begin
        inc(z2);
        if x>=2 then x:=x-2
        else if y>=2 then y:=y-2
        else if y>=1 then dec(y);
      end;
  for i:=1 to 14 do
    if c[i]=3 then
     begin
       inc(z);
       if x>=1 then dec(x)
       else if y>=1 then dec(y);
     end;
  ans:=min(ans,now+z+z2+x+y);//先处理只打出一个点数的情况
  for i:=1 to 8 do//单顺
    begin
      for j:=i to 12 do
        begin
          dec(c[j]);
          if c[j]<0 then break;
          if j-i>=4 then dfs(now+1);
        end;
      for k:=i to j do inc(c[k]);
    end;
  for i:=1 to 10 do//二顺
    begin
      for j:=i to 12 do
        begin
          dec(c[j],2);
          if c[j]<0 then break;
          if j-i>=2 then dfs(now+1);
        end;
      for k:=i to j do inc(c[k],2);
    end;
  for i:=1 to 11 do//三顺
    begin
      for j:=i to 12 do
        begin
          dec(c[j],3);
          if c[j]<0 then break;
          if j-i>=1 then dfs(now+1);
        end;
      for k:=i to j do inc(c[k],3);
end;
end;
begin
  readln(m,n);
  for t:=1 to m do 
    begin
      fillchar(c,sizeof(c),0);
      for i:=1 to n do
        begin
          readln(y,x);
          if y=0 then inc(c[14])
          else if y=1 then inc(c[12])
          else if y=2 then inc(c[13])
          else inc(c[y-2]);
        end;
      ans:=14;
      dfs(0);
      writeln(ans);
end;
end.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值