一、对课程的认识和自我感受
从入学开始到现在也有两个学期了,在刚入学那会,就有计算机的学长在群里说ACM的事情,他们说如果可以拿个省奖,那么毕业后工作或者是保研考研都行容易,也许是被这些利益好处所驱使,我对ACM有了兴趣。之后又听说,ACM是我们班主任费老师搞的,我心中对它ACM的期望有高了几分。
入学后,加入了ACM协会,开始听学长学姐讲ACM的知识,听了之后才发现,ACM好难,各种算法思路,宛如听天书一样。但还好多多少少能听懂一点,再加上自己学习信息学奥赛一本通,勉强还能跟上。之后就有了ACM新生赛,我参加了,成绩还可以得了个二等奖,让我对自己能做ACM有了信心。
寒假里,一直在纠结要不要选费老师的ACM课,一是因为自己当时想去蹭课,不想考试,毕竟费老师的考试可不是简简单单就能过的。二是对自己缺乏信心,不确定自己能学好。三是寒假里自己比较懒,不想多做一些东西。但自己终归选了费老师的课,搏一搏单车变摩托,怀着搏一搏的心思选上了费老师的课。但是在寒假里,费老师让每天写一篇博客,自己开始时还能坚持每天晚上学一学,写一写,但是几天之后,寒假惰性来了,啥都不想干就想玩,然后再也没有坚持下去。
费老师的ACM课是很好的,能学很多知识,但是也需要付出更多的时间。ACM课会讲很多内容,又多又难,需要提前预习,和及时课下复习。刚开始时,课程难度不是太难,我学起来还可以,还是可以听懂会做题的。到后来,我觉得难度变大了,一些算法不是太好理解,再加之没有及时复习预习,导致自己逐渐听不懂课程,这样几周下去,自己便有了放弃的打算。一是因为自己真的有点跟不上了。二是自己学习能力是有的,但是学习时间比较长,学习效率低下,要学课程太多太难(高数、电子、大物),自己很难兼顾。三是自己也学了一些关于ACM的知识:队列(queue)、优先队列(priority_queue)、set、multiset、map、multimap、vector、贪心算法、动态规划、区间dp、背包问题(01背包、完全背包、多重背包、分组背包)、递归函数、搜索(广搜(队列)、深搜(递归、栈)、数据结构(堆、队列、树及二叉树)、图论算法(图的遍历、基本概念、最短路径算法、图的连通性问题、并查集、最小生成树(PRIM算法、KRUSKAL算法)、拓扑排序算法)、树状数组(单点更新、区间查询;区间更新,单点查询;区间更新,区间查询)、线段树(单点更新、成段更新、区间合并、扫描线)等等这些知识,觉得受益匪浅。
总而言之,觉得自己没有能坚持下来的毅力和足够充分的时间、强大的学习能力,所以止步于此。
二、例题解析
1、Ugly numbers are numbers whose only prime factors are 2, 3 or 5. The sequence
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, …
shows the first 10 ugly numbers. By convention, 1 is included.
Given the integer n,write a program to find and print the n’th ugly number.
Input
Each line of the input contains a postisive integer n (n <= 1500).Input is terminated by a line with n=0.
Output
For each line, output the n’th ugly number .:Don’t deal with the line with n=0.
方法一:优先队列
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
long long count=0,n;
const int b[3]={2,3,5};
priority_queue<long long,vector,greater>p;
sets;
set::iterator t;
s.insert(1);
p.push(1);
while(1)
{
long long x=p.top();
p.pop();
count++;
if(count1500)
break;
long long a;
for(int i=0;i<3;i++)
{
a=xb[i];
if(s.count(a)0)
{
s.insert(a);
p.push(a);
}
}
}
t=s.begin();
while(cin>>n)
{
if(n0)
break;
else
{
for(int j=0;j<n-1;j++)
{
t++;
}
cout<<t<<endl;
t=s.begin();
}
}
}
方法二:暴力
#include
#include
using namespace std;
int main()
{
int m2=0;
int m3=0;
int m5=0;
long long a[1500];
a[0]=1;
long long tmp;
for(int i=1;i<1500;i++)
{
//分别给上一次比较得到的数乘相应的倍数再比较
tmp=2a[m2]>3a[m3]?3a[m3]:2a[m2];
tmp=tmp>5a[m5]?5*a[m5]:tmp;
a[i]=tmp;//比较得到的较小数存入数组
//满足条件的m加1,指向新得到的小数
if(tmp2a[m2]) m2++;
if(tmp==3a[m3]) m3++;
if(tmp5*a[m5]) m5++;
}
for(int i=0;i<1500;i++)
cout<<a[i]<<" ";
return 0;
}
2、贪心问题,贪时间,先比较结束时间,小的在前面排序,如果相同,在比较开始时间,小的在前面,这样省时间。
Input
输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据Ti_s,Ti_e (1<=i<=n),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。n=0表示输入结束,不做处理。
Output
对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行。
#include
#include
#include
#include
#include
#include
using namespace std;
struct mmp
{
int kai;
int end;
}a[105];
bool cmp(mmp a,mmp b)
{
if(a.end!=b.end)
return a.end<b.end;
return a.kai<b.kai;
}
int main()
{
int n;
while(cin>>n)
{
int count=1;
if(n0)
break;
for(int i=0;i<n;i++)
{
cin>>a[i].kai>>a[i].end;
}
sort(a,a+n,cmp);
for(int j=0,k=j;j<n,k<n-1;)
{
if(a[j].end<=a[k+1].kai)
{
count++;
j=k+1;
k++;
}
else
{
k++;
}
}
cout<<count<<endl;
}
}
3、馅饼题,两种解法,一是数塔,一是区间dp
都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以gameboy马上卸下身上的背包去接。但由于小径两侧都不能站人,所以他只能在小径上接。由于gameboy平时老呆在房间里玩游戏,虽然在游戏中是个身手敏捷的高手,但在现实中运动神经特别迟钝,每秒种只有在移动不超过一米的范围内接住坠落的馅饼。
为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时gameboy站在5这个位置,因此在第一秒,他只能接到4,5,6这三个位置中其中一个位置上的馅饼。问gameboy最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)
Input
输入数据有多组。每组数据的第一行为以正整数n(0<n<100000),表示有n个馅饼掉在这条小径上。在结下来的n行中,每行有两个整数x,T(0<T<100000),表示在第T秒有一个馅饼掉在x点上。同一秒钟在同一点上可能掉下多个馅饼。n=0时输入结束。
Output
每一组输入数据对应一行输出。输出一个整数m,表示gameboy最多可能接到m个馅饼。
提示:本题的输入数据量比较大,建议用scanf读入,用cin可能会超时。
方法一: 树塔
#include
#include
#include
using namespace std;
int maxi(int a,int b,int c)
{
int max1;
max1=a>b?a:b;
max1=max1>c?max1:c;
return max1;
}
int c[100001][11];
int main()
{
int i,j;
int n,a,b;
while(cin>>n&&n)
{
int m=0;
memset(c,0,sizeof©);
for(i=0;i<n;i++)
{
cin>>a>>b;
c[b][a]++;
if(m<b)
m=b;
}
for(i=m-1;i>=0;i–)
{
for(j=1;j<=9;j++)
c[i][j]+=maxi(c[i+1][j-1],c[i+1][j],c[i+1][j+1]);
c[i][0]+=max(c[i+1][0],c[i+1][1]);
c[i][10]+=max(c[i+1][10],c[i+1][9]);
}
cout<<c[0][5]<<endl;
}return 0;
}
方法二:区间dp
#include
using namespace std;
const int MAXN = 100005;
int n, a[MAXN][15], dp[MAXN][15];
int main()
{
while (~scanf_s("%d", &n) && n)
{
memset(dp, 0, sizeof(dp));
memset(a, 0, sizeof(a));
int T = 0;
for (int i = 0; i < n; i++)
{
int x, t; scanf_s("%d%d", &x, &t); x++;
a[t][x]++;
T = max(T, t);
}
for (int i = T; i >= 0; i–)
{
for (int j = 1; j <= 11; j++)
{
dp[i][j] = max(dp[i+1][j-1], max(dp[i+1][j], dp[i+1][j+1]));
dp[i][j] += a[i][j];
}
}
printf("%d\n", dp[0][6]);
}
return 0;
}
三、知识点
1、STL
栈:头文件: #include
定义:stack<data_type> stack_name;
如:stack s;
操作:
empty() – 返回bool型,表示栈内是否为空 (s.empty() )
size() – 返回栈内元素个数 (s.size() )
top() – 返回栈顶元素值 (s.top() )
pop() – 移除栈顶元素(s.pop(); )
push(data_type a) – 向栈压入一个元素 a(s.push(a); )
队列:头文件: #include
定义:queue <data_type> queue_name;
如:queue q;
操作:
empty() – 返回bool型,表示queue是否为空 (q.empty() )
size() – 返回queue内元素个数 (q.size() )
front() – 返回queue内的下一个元素 (q.front() )
back() – 返回queue内的最后一个元素(q.back() )
pop() – 移除queue中的一个元素(q.pop(); )
push(data_type a) – 将一个元素a置入queue中(q.push(a); )
vector:头文件: #include
定义:vector <data_type> vector_name;
如:vector v;
操作:
empty() – 返回bool型,表示vector是否为空 (v.empty() )
size() – 返回vector内元素个数 (v.size() )
push_back(data_type a) 将元素a插入最尾端
pop_back() 将最尾端元素删除
v[i] 类似数组取第i个位置的元素(v[0] )
set、multiset:头文件: #include
定义:set <data_type> set_name;
如:set s;//默认由小到大排序
操作:
s.insert(elem) – 安插一个elem副本,返回新元素位置。
s.erase(elem) – 移除与elem元素相等的所有元素,返回被移除 的元素个数。
s.erase(pos) – 移除迭代器pos所指位置上的元素,无返回值。
s.clear() – 移除全部元素,将整个容器
操作:
s.size() – 返回容器大小。
s.empty() – 返回容器是否为空。
s.count(elem) – 返回元素值为elem的元素的个数。
s.lower_bound(elem) – 返回 元素值>= elem的第一个元素位置。
s.upper_bound(elem) – 返回元素值 > elem的第一个元素的位置
s.begin() – 返回一个双向迭代器,指向第一个元素。
s.end() – 返回一个双向迭代器,指向最后一个元素的下一 个位置
map、multimap:头文件: #include
ACM课程报告
最新推荐文章于 2020-06-13 15:36:05 发布