Noip2012普及组

T2:

传说很遥远的藏宝楼顶层藏着诱人的宝藏。小明历尽千辛万苦终于找到传说中的这个藏宝楼,藏宝楼的门口竖着一个木板,上面写有几个大字:寻宝说明书。说明书的内容如下:
藏宝楼共有N+1层,最上面一层是顶层,顶层有一个房间里面藏着宝藏。除了顶层外,藏宝楼另有N层,每层M个房间,这M个房间围成一圈并按逆时针方向依次编号为0,…,M-1。其中一些房间有通往上一层的楼梯,每层楼的楼梯设计可能不同。每个房间里有一个指示牌,指示牌上有一个数字x,表示从这个房间开始按逆时针方向选择第x个有楼梯的房间(假定该房间的编号为k),从该房间上楼,上楼后到达上一层的k号房间。比如当前房间的指示牌上写着2,则按逆时针方向开始尝试,找到第2个有楼梯的房间,从该房间上楼。如果当前房间本身就有楼梯通向上层,该房间作为第一个有楼梯的房间。
寻宝说明书的最后用红色大号字体写着:“寻宝须知:帮助你找到每层上楼房间的指示牌上的数字(即每层第一个进入的房间内指示牌上的数字)总和为打开宝箱的密钥”。
请帮助小明算出这个打开宝箱的密钥。
输入输出格式
输入格式:
第一行2个整数N和M,之间用一个空格隔开。N表示除了顶层外藏宝楼共N层楼,M表示除顶层外每层楼有M个房间。
接下来N*M行,每行两个整数,之间用一个空格隔开,每行描述一个房间内的情况,其中第(i-1)*M+j行表示第i层j-1号房间的情况(i=1,2,…, N;j=1,2,…,M)。第一个整数表示该房间是否有楼梯通往上一层(0表示没有,1表示有),第二个整数表示指示牌上的数字。注意,从j号房间的楼梯爬到上一层到达的房间一定也是j号房间。
最后一行,一个整数,表示小明从藏宝楼底层的几号房间进入开始寻宝(注:房间编号从0开始)。


输出格式:
输出只有一行,一个整数,表示打开宝箱的密钥,这个数可能会很大,请输出对20123取模的结果即可。


输入输出样例
输入样例#1:

2 3
1 2
0 3
1 4
0 1
1 5
1 2
1
输出样例#1:
5

【数据范围】
对于50%数据,有0<N≤1000,0<x≤10000;
对于100%数据,有0<N≤10000,0<M≤100,0<x≤1,000,000。



这道题目其实就是一道模拟题,其中如果用纯模拟的方法只能得50分,所以可以优化。

判断到达哪个楼梯房间的时候可以用周期的原理,只求取模后的值来模拟即可——这样就可以大大的减少复杂度了。

代码:

var
         bz,num:array[0..10000,0..100] of longint;
         c:array[0..10000] of longint;
         i,j,n,m,w,ans,sum:longint;
begin
         assign(input,'treasure.in'); reset(input);
         assign(output,'treasure.out'); rewrite(output);

         readln(n,m);
         for i:=1 to n do
                  for j:=1 to m do
                  begin
                           readln(bz[i,j],num[i,j]);
                           if bz[i,j]=1 then inc(c[i]);
                  end;
         readln(w);  inc(w);
         for i:=1 to n do
         begin
                  ans:=(ans+num[i,w]) mod 20123;
                  j:=(num[i,w]-1) mod c[i]+1;
                  sum:=bz[i,w];
                  while sum<>j do
                  begin
                           w:=w mod m+1;
                           inc(sum,bz[i,w]);
                  end;
         end;
         writeln(ans);

         close(input);close(output);
end.


当然,其实上面代码的时间复杂度为O(nm),但如果M再大一点的话,即需要O(n)的算法——可以再优化一下,把取模后的值在判断是哪个楼梯的时候可以优化,优化成O(1)的计算,a[i,t]的值就是当前是在哪个楼梯,主要是注意f[i,j]表示的是第i层到第j个房间截止有多少个楼梯房间。f[i,w]+(tot[i,w] mod a[i,0])再取模就是要求的值了。其次还需要注意的是,有可能t的值=0,这时候w就等于第i层最后一个房间——因为等于1的时候是第一个,=0不就是最后一个?

代码(但实质上对于这道题的数据时间并没有体现多少,如果m大一点的话则必须用此方法):

var
        n,m,i,j,w,ans,t:longint;
        bz:array[1..10000,0..100] of 0..1;
        a,tot,f:array[1..10000,0..100] of Longint;
begin
        assign(input,'treasure.in'); reset(input);
        assign(output,'treasure.out'); rewrite(output);

        readln(n,m);
        for i:=1 to n do
                for j:=1 to m do
                begin
                        readln(bz[i,j],tot[i,j]);
                        f[i,j]:=f[i,j-1];
                        if bz[i,j]=1 then
                        begin
                                inc(a[i,0]);
                                a[i,a[i,0]]:=j;
                                inc(f[i,j]);
                        end;
                end;
        readln(W);
        inc(w);
        for i:=1 to n do
        begin
                ans:=(ans+tot[i,w]) mod 20123;
                if bz[i,w]=1 then dec(tot[i,w]);
                t:=((tot[i,w] mod a[i,0])+f[i,w]-1) mod a[i,0]+1;
                if t<>0 then w:=a[i,t] else w:=a[i,a[i,0]];
        end;
        writeln(ans);

        close(input); close(output);
end.



T2:

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆。通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号。为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。


输入输出格式
输入格式:
第一行包含两个正整数n和m,中间用一个空格隔开。
第二行有n个整数,每两个整数之间用一个空格隔开,依次表示a1、a2、……an。

输出格式:
输出只有一行,一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对1000007取模的结果。


输入输出样例
输入样例#1:
2 4
3 2
输出样例#1:
2

说明
【数据范围】
对于20%数据,有0<n≤8,0<m≤8,0≤ai≤8;
对于50%数据,有0<n≤20,0<m≤20,0≤ai≤20;
对于100%数据,有0<n≤100,0<m≤100,0≤ai≤100。


这道题目就是赤裸裸的DP,状态也很容易想到——设f[i,j]表示摆i种花在前j个位置上的方案数,显然f[i,j]:=f[i,j]+f[i-1,j-k]。边界f[i,0]:=1;


代码:

var
        a:array[1..100] of Longint;
        f:array[0..100,0..100] of Longint;
        n,m,i,j,k:Longint;
function min(x,y:LONgint):Longint;
begin
        if x<y then exit(x) else exit(y);
end;

begin
        assign(input,'flower.in'); reset(input);
        assign(output,'flower.out'); rewrite(output);

        read(n,m);
        for i:=1 to n do
                read(a[i]);
        for i:=0 to n do
                f[i,0]:=1;
        for i:=1 to n do
                for j:=1 to m do
                        for k:=0 to min(j,a[i]) do
                                f[i,j]:=(f[i,j]+f[i-1,j-k]) mod 1000007;
        writeln(f[n,m]);

        close(input); close(output);
end.



T4:

有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一种文化超过一次(即如果他学习了某种文化,则他就不能到达其他有这种文化的国家)。不同的国家可能有相同的文化。不同文化的国家对其他文化的看法不同,有些文化会排斥外来文化(即如果他学习了某种文化,则他不能到达排斥这种文化的其他国家)。现给定各个国家间的地理关系,各个国家的文化,每种文化对其他文化的看法,以及这位使者游历的起点和终点(在起点和终点也会学习当地的文化),国家间的道路距离,试求从起点到终点最少需走多少路。

输入输出格式
输入格式:
第一行为五个整数 N,K,M,S,T,每两个整数之间用一个空格隔开,依次代表国家
个数(国家编号为 1 到 N),文化种数(文化编号为 1 到 K),道路的条数,以及起点和终点的编号(保证 S 不等于 T);
第二行为 N 个整数,每两个整数之间用一个空格隔开,其中第 i 个数 Ci,表示国家 i的文化为 Ci。
接下来的 K 行,每行 K 个整数,每两个整数之间用一个空格隔开,记第 i 行的第 j 个数为 aij,aij= 1 表示文化 i 排斥外来文化 j(i 等于 j 时表示排斥相同文化的外来人),aij= 0 表示不排斥(注意 i 排斥 j 并不保证 j 一定也排斥 i)。
接下来的 M 行,每行三个整数 u,v,d,每两个整数之间用一个空格隔开,表示国家 u与国家 v 有一条距离为 d 的可双向通行的道路(保证 u 不等于 v,两个国家之间可能有多条道路)。

输出格式:输出只有一行,一个整数,表示使者从起点国家到达终点国家最少需要走的距离数(如果无解则输出-1)。

输入输出样例
输入样例#1:
输入输出样例 
1

2 2 1 1 2 
1 2 
0 1 
1 0 
1 2 10 

输入输出样例 
2

2 2 1 1 2 
1 2 
0 1 
0 0 
1 2 10 


输出样例#1:
输入输出样例 
1

-1 

输入输出样例 
2

10


说明
输入输出样例说明1
由于到国家 2 必须要经过国家 1,而国家 2 的文明却排斥国家 1 的文明,所以不可能到达国家 2。输入输出样例说明2路线为 1 -> 2

【数据范围】
对于 100%的数据,有 2≤N≤100 1≤K≤100 1≤M≤N2 1≤ki≤K 1≤u, v≤N 1≤d≤1000 S≠T 1≤S,T≤N


这道题数据非常的坑,又坑又烂,例如,“正确”的dfs倒过来搜就行了,这里正确为什么要打引号?因为题目中有一个规定就是不能学过重复的文化,还有就是每个文化之间都不能排斥,而不仅仅是两两城市之间不排斥,而这两个规定即使不加同样能AC——因为没有了这个两个规定,即这道题用Floyd也可以做对了!——但是这道题的正解我认为是spfa,因为数据如果大一点,强悍一点,Dfs是过不了的,而floyd答案是错误的,只有spfa才能做对,spfa里面需要很多的判重。

例如——dis[i].bz是判断不能学重复的文化,而dis[i].xuan是选中的文化不能排斥。如果没了这两个判重真不知难点在哪里,然而就算有这两个判重好像这也不是难点吧。


spfa算法的方法:

type
        trr=array[0..100] of boolean;
        arr=array[0..100] of longint;
var
        c:array[1..100] of longint;
        a:array[1..100,0..1000] of longint;
        bz,d:array[1..100,1..100] of longint;
        data:array[1..1000] of longint;
        f:array[1..100] of boolean;
        dis:array[1..100] of record way:longint; xuan:arr; bz:trr end;
        n,k,m,s,t,i,j,x,y,way,head,tail,w:longint;
function pd(x:arr; k:longint):boolean;
var
        i:LONGINT;
begin
        for i:=1 to x[0] do
                if (bz[c[k],c[x[i]]]=1) then exit(false);
        exit(true);
end;
begin
        assign(input,'culture.in'); reset(input);
        assign(output,'culture.out'); rewrite(output);


        readln(n,k,m,s,t);
        for i:=1 to n do
                read(c[i]);
        for i:=1 to k do
        begin
                for j:=1 to k do
                        read(bz[i,j]);
                readln;
        end;
        fillchar(d,sizeof(d),5);
        for i:=1 to m do
        begin
                read(x,y,way);
                inc(a[x,0]);
                a[x,a[x,0]]:=y;
                if way<d[x,y] then d[x,y]:=way;


                inc(a[y,0]);
                a[y,a[y,0]]:=x;
                if way<d[y,x] then d[y,x]:=way;
        end;


        head:=0;
        tail:=1;
        fillchar(dis,sizeof(dis),5);
        dis[s].way:=0;
        dis[s].xuan[0]:=1;
        dis[s].xuan[1]:=s;
        data[1]:=s;
        while head<tail do
        begin
                inc(head);
                w:=data[head];
                for i:=1 to a[w,0] do
                        if pd(dis[w].xuan,a[w,i]) and (dis[a[w,i]].way>dis[w].way+d[w,a[w,i]]) and dis[w].bz[c[a[w,i]]] then
                        begin
                                dis[a[w,i]]:=dis[w];
                                dis[a[w,i]].way:=dis[a[w,i]].way+d[w,a[w,i]];
                                dis[a[w,i]].bz:=dis[w].bz;
                                dis[a[w,i]].bz[c[a[w,i]]]:=false;
                                inc(dis[a[w,i]].xuan[0]);
                                dis[a[w,i]].xuan[dis[a[w,i]].xuan[0]]:=a[w,i];
                                if not f[a[w,i]] then
                                begin
                                        f[a[w,i]]:=true;
                                        inc(tail);
                                        data[tail]:=a[w,i];
                                end;
                        end;
                f[w]:=false;
        end;


        if dis[t].way=84215045 then writeln(-1) else writeln(dis[t].way);


        close(input); close(output);
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值