嗯下定决心要好好做CF了。
总结:
这次做出了三道题,也是做cf以来做出题目次数最多的一次,想想真是有点小激动呢。不过从理性上来说,这次的前三道题实在是太水了,我水平比较渣也是很容易就做了出来,不信的话看下面的题目描述。
Problem A:
题目描述:
一道关于看电影的题,主人每次有两个选项,一是看这分钟,二是跳过x分钟。我们的要求是有一些电影的时刻必须要看,所以这就难免了有一些非必须的时间不能够跳过。现在要计算的是最少要看多长时间的电影,保证必须要看的时刻全部被包括在内。
算法思想:
简直没有什么算法在其中。我们需要做的就是从1到电影结束进行枚举,用加减乘除mod运算来把需要的时间加起来。
cur代表起始时刻,然后(l-cur) % x 是算出要有多少非必须的时刻要看。然后算res,更改cur值,十分简单。
代码部分:
#include <iostream> #include <algorithm> using namespace std; int n, x; int main() { cin >> n >> x; int res = 0; int cur = 1; for (int i = 0; i < n; i++) { int l, r; cin >> l >> r; int a = (l - cur) % x; res = (r-l + 1) + a + res; cur = r + 1; } cout << res << endl; return 0; }
Problem B:
题目描述:
语境是这个人会两种语言,然后他要take notes,然后他要选择写的字最少的哪种语言来记。
那么给定的是几个一一对应的词,然后最后的时候给一句话。让你输出应该如何去记笔记。
算法思想:
开始的时候想用map<string, string>直接干掉,但是结果发现比这个还要复杂一点,原因在于最后给话的时候不知道是mapped value还是key value,所以我就很贱的开了两个map,相当于双向记录。卧槽现在想想都觉得贱。
其余没有什么好讲的,无非就是输入话的时候判断在哪个map里面然后判断长度输出。然后打印的时候注意其是不是最后一个这样。
代码部分:
#include <iostream> #include <map> #include <string> using namespace std; int n, m; int main() { cin >> n >> m; map<string, string> mymap; map<string, string> mymap2; for (int i = 0; i < m; ++i) { string s1, s2; cin >> s1 >> s2; mymap.insert(make_pair(s1, s2)); mymap2.insert(make_pair(s2, s1)); } for (int i = 0; i < n; ++i) { string s; cin >> s; if (mymap.count(s)) { if (s.length() <= mymap[s].length()) { if (i!=n-1) cout << s << " "; else cout << s; } else { if (i!=n-1) cout << mymap[s] << " "; else cout << mymap[s]; } } else { if (s.length() < mymap2[s].length()) { if (i!=n-1) cout << s << " "; else cout << s; } else { if (i!=n-1) cout << mymap2[s] << " "; else cout << mymap2[s]; } } } return 0; }
Problem C:
题目概述:
第一次读题把我吓尿了,又是直线又是几何什么的东西,感觉都没有学过的样子。
然后又看了一下图....就惊觉“卧槽这么简单”。
题目描述的大概是一个镇子被各种直线分割,然后给定两个点,求从这个点到另一个点要跨过几条直线。
算法思想:
这就是高中的线性规划嘛 = = ,如果两个点在直线的两侧那么一定会把需要跨过的直线加一,那么这样的话只需要知道如何计算两个点是不是在直线的异侧就好了。那么方法就是把点带入直线看正负啦。
不过值得一提的是,在下面的代码的时候,我第一次判断是(suba*subb<0),卧槽然后这样就放肆越界,简直是跪得一塌糊涂。因为这个错了三次加上晚交半个小时,不然就冲破1600了。唉期盼下次吧。
代码部分:
#include <iostream> using namespace std; int main() { int a, b, c, d; cin >> a >> b >> c >> d; int n; cin >> n; int res = 0; for (int i = 0; i < n; ++i) { long long p, q, r; cin >> p >> q >> r; long long suba = p*a + q*b + r; long long subb = p*c + q*d + r; if (suba > 0 && subb < 0) res++; if (suba < 0 && subb > 0) res++; } cout << res << endl; return 0; }
Problem D:
题目概述:
应该是挺典型的DP,但是我当时老想着DFS就跪了。
题目叙述的比较复杂。首先给要猜几个东西,以及最多能猜多少次。然后大概是给定一个题目能猜出来的概率,以及单个题目最多能够猜的次数,超过了这个次数就一定能够才出来(简直是超能力),然后有n个这样的list,最后算期望。
也是典型概率题吧,要是单个数据的话完全可以暴力算出来。
算法思想:
对于我来说真的好难,我一步一步读程序加上慎重思考用了几个小时才模糊地明白,可能是DP的思想根本不够深刻,所以导致我没有能够完全理解。
首先输入n,t,p,x的数据,这个没有什么可说的,p是概率,x是最多猜几次。
dp[i][j]记录下来的是猜到第 i 首恰好用了时间 j 的概率,那么从dp[1][1]到dp[2][2]实际上是说,我在猜第一首的时候恰好用了时间1,那么我由猜出一首恰好用了时间2,根据这个思想,才能推出最后的结果是所有dp数组累加的结果。
我想这个print的结果想了好几天,如果有高人能够点醒我希望千万不要吝惜评论。
值得一说的是,在循环中pre这个变量的意思是之前没有猜出来。然后如果是猜出来了就是p*pre,然后把pre变成pre*(1-p) + dp[i-1][j],意为没有猜出来。
还有在j>=x[i]的那段代码里也要时使劲看一下才能琢磨出来。pow是math里面的函数,求的是(1-p)^k。
这道题是div2里面做出的人最少的一题了,应该比E还要难。不说别的了,只是希望能够多看到几次类似的题,让我关键时候可以做出来。
代码部分:
#include <iostream> #include <cmath> #include <iomanip> using namespace std; double dp[5002][5002] = { {0} }; int x[5002]; double p[5002]; int main() { int n, t; cin >> n >> t; // input all the values; for (int i = 1; i <= n; i++) { double p_; cin >> p_; p[i] = p_ / 100; cin >> x[i]; } dp[0][0] = 1; for (int i = 1; i <= n; ++i) { double pre = dp[i - 1][0]; for (int j = 1; j <= t; ++j) { dp[i][j] = p[i] * pre; pre = pre*(1 - p[i]) + dp[i - 1][j]; if (j >= x[i]) { pre -= dp[i - 1][j - x[i]] * pow(1 - p[i], x[i]); dp[i][j] += dp[i - 1][j - x[i]] * pow(1-p[i],x[i]); } } } double res = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= t; ++j) { res += dp[i][j]; } } cout << fixed << setprecision(10) << res << endl; return 0; }
Problem E: