这次周赛还是打成了一坨翔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;
}
C题很水就不说了。
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;
}
E题:模拟题。。但是我还是回去搞了好久,一直有点小问题。。。醉。
一开始分析错了,以为整个矩阵的宽度就是最大的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;
}