这次周赛还是打成了一坨翔orzzzzzz。就出了两题。一开始看了A题,然后结果发现好像不是特别好做,看到有出了B,去看了B然后想了个脑残贪心,发现过不了,这时好多人过了C然后赶紧去看,结果确实是一个水题,赶紧写了交1Y,回来搞B结果B搞了好久,发现不是贪心,应该是类似完全背包的东西。还好过了,4Y。。。结果发现D应该比A题容易点,去写了D,发现一直会超时,感觉不知道在哪里优化。。。。期间还看了一下A,还是没什么思路,感觉是贪心又不肯定。还是搞D,直到周赛结束。两题悲剧。。。
A:http://codeforces.com/problemset/problem/437/B
题目定义了一个lowbit 其实就是将这个数字转化成二进制数之后看从右往左第一个出现的1在哪,它加上他右边的所有0就是这个lowbit的二进制表示,然而很容易推出对于每个十进制数的lowbit就是这个数能整除二 n次,这个lowbit就是2^n。这样就知道了lowbit,然后题目给了一个sum和一个limit,就是表示在1~limit的范围中的所有中任意选几个的lowbit,是否能组合成一个sum。(当然每个只能用一次)
然后就是运用一个排序,从大到小,贪心,找出是否能凑成sum,这一步就是在周赛的时候没有想到了,为什么没有想到呢,我思考过是否能运用贪心来做,但是感觉lowbit都是2^n,然而这个不完全是有序的,中间可能穿插了很多1,这样直接遍历的话应该是不行的,感觉没有想到从大往小的取,为什么这样是可以的呢,因为在lowbit数组中,大的数肯定是出现的次数肯定是比较少的。那么我们就先取大的往下取,如果加上这个数超过和的话就抛弃他,这样一定能找出是否能凑成sum。
这也确实体现了贪心思想也总是跟排序在一起的,也是个经验教训,思考问题的时候可能要多尝试,努力往前进一步。想运用贪心的时候却感觉不可以的时候,思考一下是否需要排序解决。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define M 100000 struct node { int num,lb; }s[M]; int ans[M]; int cmp(node a,node b) { return a.lb > b.lb; } int main() { int n,m; while(scanf("%d %d",&n,&m)==2) { for(int i = 1;i <= m;i++) { int temp = 0; int a = i; while(a%2==0) //求lowbit { temp++; a = a/2; } s[i].num = i; s[i].lb = pow(2,temp); } sort(s+1,s+m+1,cmp); int sum = 0; int ok = 0; int a = 0; for(int i = 1;i <= m;i++) { if(sum + s[i].lb>n) continue; //抛弃这个数 else { sum += s[i].lb; //贪心 ans[a++] = s[i].num; } if(sum == n) { ok = 1; break; } } if(!ok) { printf("-1\n"); continue; } printf("%d\n%d",a,ans[0]); for(int i = 1;i < a;i++) { printf(" %d",ans[i]); } printf("\n"); } return 0; }
B:http://acm.timus.ru/problem.aspx?space=1&num=1073
题意:就是给一个数n,把这个数拆成几个平方数之和,求最少要几个。
一开始用了贪心不可以做出来,后来改成类似完全背包的做法了,因为可以直接求出来有几种平方数,然而这每一种都可以用无限次,那么就是完全背包咯。
不过这里求的是最少几个,修改一下状态转移方程。
c[i] 表示第i个平方数 值为i*i;
定义p[i]表示的是i能拆成几个最少几个平方数的和,转移方程:p[j] = min(p[j],p[j-c[i]]+1); 就只有两种状态转移过来,一种是要这个平方数,另一种是不要。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define M 60009 #define INF 0x3f3f3f3f int c[M]; int p[M]; int main() { int n; while(scanf("%d",&n)==1) { if(n<=0) { printf("0\n"); continue; } for(int i = 0;i <= n;i++) { p[i] = INF; } p[0] = 0; for(int i = 1;i*i <= n;i++) { c[i] = i*i; } for(int i = 1;i*i <= n;i++) { for(int j = c[i];j <= n;j++) { p[j] = min(p[j],p[j-c[i]]+1); } } printf("%d\n",p[n]); } return 0; }
D:http://codeforces.com/problemset/problem/75/C
就是给两个数,然后再给几个询问,对于每个询问,两个数字,求在这两个数字之间的最大的公约数。
求出这两个数的最大公约数,然后求出这个最大公约数的所有因数就是这两个数的所有公约数。都存入一个set中
比赛的时候想到这里都还是对的,但是我在对每个区间进行查找的时候,我直接从大到小遍历查这个数在不在set里orzzzzzz,结果肯定跪了。。
应该运用二分查找,这次才知道set里本身就有二分的成员函数。。
为什么没有想到用二分查找呢,感觉是对于题目的分析还不够深入,如果找出第一个大于high的值,往回找一个如果在low跟high之间的话,那么就有,否则就没有。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <set> using namespace std; #define M 100000 int f[M]; int main() { int n,m; while(scanf("%d %d",&n,&m)==2) { if(n>m) swap(n,m); set<int> ss; ss.insert(1); int a = m,b = n; while (a % b != 0) { int temp = a % b; a = b; b = temp; } //printf("%d\n",b); ss.insert(b); int temp = b; for(int i = 2;i*i <= temp;i++) //循环遍历到根号 { if(temp%i==0) //运用匹配的方法就可以一次找出两个。 { ss.insert(i); ss.insert(temp/i); } } int t; scanf("%d",&t); while(t--) { int a,b; int ok = 0; scanf("%d %d",&a,&b); set<int>::iterator it; it = upper_bound(ss.begin(),ss.end(),b);//后来才了解到set本身就有成员函数 upper_bound lower_bound()括号里为要查询的数 if(*(--it)>=a) printf("%d\n",*it); else printf("-1\n");; } } return 0; }
一开始分析错了,以为整个矩阵的宽度就是最大的y,发现并不是的。如果上升的话记为加,下降记为减,把这些值依次加起来,找出最小的,还有最大的,这两个差值就是宽度。然后我找出了最大的那个所在序列的位置,也就是最高峰。然后往右开始画,再往左。
不过这样好麻烦orzzzz,看了别人的发现好容易orzzzz,因为和不可能超过1000,所以将y初始化为1000,如果是上升就--,下降就++,记录下最小的y和最大的y,并且顺便将上升记成一种,下降记成一种。从最小的遍历到最大的就可以遍历所有的行了。。(不过都要注意的是在转折处不要加减了)
代码:方法一
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define M 1009 #define INF 0x3f3f3f3f int s[M]; char map[M][M]; int main() { int n; while(scanf("%d",&n)==1) { int max1 = -INF; int max2 = -INF; int a = 0; int st = 0; int sum = 0; int temp = 0; int low = 0; for(int i = 0;i < n;i++) { scanf("%d",&s[i]); max2 = max(max2,s[i]); sum += s[i]; if(i%2==0) temp += s[i]; else temp -= s[i]; if(max1 < temp) { max1 = temp; a = i; st = sum; } low = min(temp,low); } memset(map,' ',sizeof(map)); int kk = 0; temp = st; for(int i = a+1;i < n;i++) { if(i%2==0) { for(int j = 0;j < s[i];j++) { if(j!=s[i]-1) map[kk--][temp++] = '/'; else map[kk][temp++] = '/'; } } else { for(int j = 0;j < s[i];j++) { if(j!=s[i]-1) map[kk++][temp++] = '\\'; else map[kk][temp++] = '\\'; } } } kk = 0; st--; for(int i = a;i >= 0;i--) { if(i%2==0) { for(int j = 0;j < s[i];j++) { if(j!=s[i]-1) map[kk++][st--] = '/'; else map[kk][st--] = '/'; } } else { for(int j = 0;j < s[i];j++) { if(j!=s[i]-1) map[kk--][st--] = '\\'; else map[kk][st--] = '\\'; } } } for(int i = 0;i < max1-low;i++) { for(int j = 0;j < sum;j++) putchar(map[i][j]); putchar('\n'); } } return 0; }二:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define M 2009 #define INF 0x3f3f3f3f int s[M]; int map[M][M]; int main() { int n; //putchar('\\'); while(scanf("%d",&n)==1) { int maxy = -INF; int miny = INF; int x = 0,y = 1000; memset(map,0,sizeof(map)); for(int i = 0;i < n;i++) { scanf("%d",&s[i]); for(int j = 0;j < s[i];j++) { if(i%2==0) { maxy = max(maxy,y); miny = min(miny,y); if(j!=s[i]-1) map[y--][x++] = 1; else map[y][x++] = 1; } else { maxy = max(maxy,y); miny = min(miny,y); if(j!=s[i]-1) map[y++][x++] = -1; else map[y][x++] = -1; } } } for(int i = miny;i <= maxy;i++) { for(int j = 0;j < x;j++) { if(map[i][j]==0) putchar(' '); else if(map[i][j]==1) putchar('/'); else putchar('\\'); } putchar('\n'); } } return 0; }