圈钱学堂7日游 - Day2 上午 *

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了圈钱学堂7日游 - Day2 上午 *相关的知识,希望对你有一定的参考价值。

此文为博主原创,转载...转载这种文章有意思吗qwq

严禁各OJ在未经博主同意的情况下擅自使用博文中的题面作为OJ题目。

今天是长者的测试www

感觉长者的题超棒的,部分分给得很足。(然而满分算法就QAQ)

 

上午【Friends】

 

遇见(1s)

题目大意:有n座楼,每座的高度为hi。你想在这些楼之间跳跃,最后跳到地面上。

每次跳跃的花费为准备代价ci+高度代价abs(ha-hb),跳到地面上时不需要花费高度代价。

问在花费不超过T的前提下,最多能跳跃多少次。不规定起点,一座楼只能跳一次。

数据范围:

1≤??≤50,1≤????,???≤106,1≤??≤107

 

分析:

我做题的时候没看出来这是DP(我太菜了QAQ),就乱写了个贪心。

预处理出cost[i][j]表示从i跳到j的花费。

枚举起点,对于每个起点,不停地尝试往花费最少的楼上跳跃,跳不过去就退出更新答案。

就这样拿到了70分!(可能数据太弱,长者没卡贪心)

正解:(很水的)DP。dp[i][j]表示当前在i,已经跳了j次的最小花费。

跳跃时应该按照一个高度单调的序列跳,这样就不存在不必要的高度代价,所以先给楼层按照高度排序。

枚举本次跳跃的目的地l,dp[l][j] = Min(dp[l][j],dp[i][j-1]+cost[i][l]).

根据我的写法,n=1时不会进入循环,所以需要特判n=1时的ans。

长者难度评估:第1.5题。

 

AC代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<map>
 5 
 6 const int MAXN = 100002;
 7 inline void read(int &x)
 8 {
 9     char ch = getchar(),c = ch;x = 0;
10     while(ch < 0 || ch > 9) c = ch,ch = getchar();
11     while(ch <= 9 && ch >= 0) x = (x<<1)+(x<<3)+ch-0,ch = getchar();
12     if(c == -) x = -x;
13 }
14 
15 int n,T,ans,dp[55][55];
16 
17 struct CITY
18 {
19     int h,c;
20 }a[55];
21 
22 int cmp(CITY a,CITY b)
23 {return a.h < b.h;}
24 
25 inline int Min(int a,int b)
26 {return a<b?a:b;}
27 
28 inline int Max(int a,int b)
29 {return a>b?a:b;}
30 
31 inline int Abs(int x)
32 {return x>=0?x:-x;}
33 
34 int main()
35 {
36     read(n);
37     for(int i = 1;i <= n;++ i)
38         read(a[i].c);
39     for(int i = 1;i <= n;++ i)
40         read(a[i].h);
41     read(T);
42     memset(dp,0x3f,sizeof(dp));
43     std::sort(a+1,a+1+n,cmp);
44     for(int i = 1;i <= n;++ i)
45         dp[i][1] = a[i].c;
46     for(int i = 1;i <= n;++ i)
47         for(int j = 1;j <= n;++ j)
48             for(int l = i+1;l <= n;++ l)
49             {
50                 dp[l][j] = Min(dp[l][j],dp[i][j-1]+a[l].h-a[i].h+a[l].c);
51                 if(dp[l][j] <= T) ans = Max(ans,j);
52             }
53     if(n == 1 && a[1].c <= T) ans = 1;
54     printf("%d\n",ans);            
55     return 0;
56 }

 

都市

题目大意:给出N个数的两两之和(共N*(N-1)/2个),求这N个数。

由于这N个数可能有多解,先输出解的个数,再按字典序输出这N组解。

数据范围:1≤??≤300,??个数均不超过 108

 

分析:

把输入的数从小到大排序。设排序后分别为x1,x2...xn*(n+1)/2,原来的n个数从小到大为a1,a2...an

容易得出x1 = a1+a2,x2 = a1+a3,但x3的大小就无法确定了(a1+a4或者a2+a3)。

假设我们知道了x3a2+a3,这样就能解出a1,a2,a3。此时x1,x2,x3是无用的,将它们打上标记,再用第一个未标记x求出x4。

一直推算下去,可以求出所有的a值。因此枚举a2+a3的大小,解出答案即可。

这题还是很有难度的。长者难度评估:第2题。

AC代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cmath>
 4 #include<cstring>
 5 
 6 const int MAXN = 302;
 7 inline void read(int &x)
 8 {
 9     char ch = getchar(),c = ch;x = 0;
10     while(ch < 0 || ch > 9) c = ch,ch = getchar();
11     while(ch <= 9 && ch >= 0) x = (x<<1)+(x<<3)+ch-0,ch = getchar();
12     if(c == -) x = -x;
13 }
14 
15 int n,m,cnt;
16 int ans[MAXN][MAXN],tmp[MAXN],use[46002],x[46002];
17 
18 void check(int now)
19 {
20     memset(use,0,sizeof(use));
21     if((x[1]+x[2]+x[now])&1) return;
22     //根据a1+a2,a1+a3,a2+a3求出a1,a2,a3 
23     tmp[1] = (x[1]+x[2]+x[now])/2-x[now];
24     tmp[2] = x[1]-tmp[1];
25     tmp[3] = x[2]-tmp[1];
26     //use标记已经用过的sum 
27     use[1] = use[2] = use[now] = 1;
28     for(int i=4,j=3;i <= n;++ i)
29     {
30         //尝试推导出a4,a5....an 
31         while(j<=m && use[j]) ++ j;
32         if(j > m) return;
33         tmp[i] = x[j]-tmp[1];
34         use[j] = true;
35         for(int k = 2;k < i;++ k)
36         {
37             if(tmp[k] > tmp[i]) return;
38             int v = tmp[k]+tmp[i];
39             int p = std::lower_bound(x+1,x+1+m,v)-x;
40             if(x[p] != v) return;
41             int pp = p;
42             while(pp && x[pp]==x[p]) -- pp;
43             ++ pp;
44             while(pp<=m && x[pp]==x[p] && use[pp])
45                 ++ pp;
46             if(x[pp] != x[p] || use[pp]) return;
47             p = pp;
48             use[p] = 1;
49         }
50     }
51     ++ cnt;
52     for(int i = 1;i <= n;++ i)
53         ans[cnt][i]=tmp[i],tmp[i]=0;
54 }
55 
56 int main()
57 {
58     freopen("city.in","r",stdin);
59     freopen("city.out","w",stdout);
60     read(n);
61     m = n*(n-1)/2;
62     for(int i = 1;i <= m;++ i)
63         read(x[i]);
64     std::sort(x+1,x+1+m);
65     for(int i = 3;i <= m;)
66     {
67         //枚举a2+a3的值 
68         check(i);
69         int nxt = i;
70         while(nxt <= m && x[nxt] == x[i]) ++ nxt;
71         i = nxt;
72     }
73     printf("%d\n",cnt);
74     for(int i = 1;i <= cnt;++ i)
75     {
76         for(int j = 1;j <= n;++ j)
77             printf("%d ",ans[i][j]);
78         printf("\n");
79     }
80     return 0;
81 }

 

街灯

题目大意:

给出一个数列,每次询问从l到r的所有数中模p等于v的有多少个。

数据范围:

1<=N,M<=100000,每个数不超过10000,1<=p<=109

分析:

根据模数p的范围进行分块处理。【还有这种操作!】

首先稳妥起见把30%的数据分治写了

因为数的大小不超过104,而p有可能到109,显然大于10000的p是没有意义的。

所以我们认为p的范围是1~10000,按照sqrt(10000)=100分块来做。

p<=100时,p,v一共只会有100*(100-1)/2种对应情况。可以预处理出这些情况。

枚举每一种(p,v),计算出所有的数字中%p=v的数放入一个vector中,询问时二分查找。

p>100时,离线处理。对于每一对(p,v),能够作为答案的数只可能有v,v+p,v+2*p,...。给每个v开一个vector,

事先把数列中所有大小为v,v+p,v+2*p,...的数放到vector里,询问时二分查找。

总时间复杂度为O(n*logn*sqrt(n))。长者难度评估:第3题

AC代码:

太麻烦了并没有写。(临考疯狂翘代码.jpg)

 

总结:

期望得分40+30+30=100,实际得分70+0+30=100.

T1的故事告诉我们要大胆贪心,不用求证。我也没想到乱搞的贪心有70分...

所以说只要脑洞够大,没有什么题是不能贪的(雾)。

长者的题好像总有一些神奇的做法...根据数据范围分块我也是第一次见,太强大了。

 

以上是关于圈钱学堂7日游 - Day2 上午 *的主要内容,如果未能解决你的问题,请参考以下文章

北京一日游(网安杯)

清北学堂Day2

Web测试实践Day2

济南清北学堂游记 Day 1.

吉林市一日游

20180925-7 规格说明书-吉林市2日游