此文为博主原创,转载...转载这种文章有意思吗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)。
假设我们知道了x3为a2+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分...
所以说只要脑洞够大,没有什么题是不能贪的(雾)。
长者的题好像总有一些神奇的做法...根据数据范围分块我也是第一次见,太强大了。