前言:这场cf我当晚给忘了,事后补的,补了4题,没有一道题是1a的,很难受,痛定思痛,发现自己的思维存在一些问题,现结合这些问题,总结ac的正确打开方式。
链接:http://codeforces.com/contest/1037
A题:
一开始看到tourist大神,一分钟就a了此题,心想肯定是规律题,于是写了前几个之后,就草草,猜了一个规律(貌似是n+C(n,2)),结果wa了,后来就老老实实结合题意进行推导了。
其实给你n个数字,进行组合,那么可以组合成(C(n,1)+C(n,2)+...+C(n,n)=2^n-1)个不同的数字,对于本题,只要这n个数字是连续的就可以了。
所以公式就是ans = log2(n)+1;
错误类型:题目理解正确,所写即所想,数据类型范围均合适,程序正常执行(未越界,未除零),代码速度达已最优,所想却为错。
解决方式:重新想算法。
代码:
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin freopen("in.txt","r",stdin)
#define Fout freopen("out.txt","w",stdout)
#define Case(T) int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b) for(int i = a; i < b; ++i)
#define fd(i,a,b) for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val) fill(a,a+n,val)
#define Scand(n) scanf("%d",&n)
#define Scand2(a,b) scanf("%d%d",&a,&b)
#define Scand3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s) scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 1e4 + 50;
const int INF = 0xffffff;
#ifndef ONLINE_JUDGE
#endif // ONLINE_JUDGE
inline ll read(){
ll sgn = 1; ll sum = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if(ch == '-') sgn = -sgn;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
sum = sum*10+(ch-'0');
ch = getchar();
}
return sgn*sum;
}
int main()
{
#ifndef ONLINE_JUDGE
//Fin;
#endif // ONLINE_JUDGE
ll n = read();
ll ans = 0;
//一开始直接模拟求解的,其实直接套公式就行
// while (pow(2, ans) < n) {
// ans++;
// }
// if((ll)pow(2, ans) == n)
// ans++;
ans = log2(n)+1;
printf("%lld\n",ans);
return 0;
}
B题:
这题tourist大神是2分钟a的,强啊,简直超人类啊。而我这个菜鸡思考了一会,发现只要先排个序,取中间的那个,然后把它朝目标s进行加减就行,需要注意的是,当中间的那个数变为s之后,此时数组可能已经不是从小到大的顺序了,为了让它保持在中间位置,就需要对它前后的数字进行处理,保证在它前面的都小于等于它(如果此时它之间的数比它大,就得把这个数减到为s),对它后面的数字进行相似的处理。
实现完代码就交了,结果wa在第十个。。。。仔细又看了代码,发现自己的代码可能数组越界,少了对下标的判断。
错误原因:
题目理解正确,所想也正确,所写即所想,数据类型范围均合适,代码速度达已最优,程序未正常执行(越界或者除零)。
解决方式:谨防下标越界。
代码:
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin freopen("in.txt","r",stdin)
#define Fout freopen("out.txt","w",stdout)
#define Case(T) int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b) for(int i = a; i < b; ++i)
#define fd(i,a,b) for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val) fill(a,a+n,val)
#define Scand(n) scanf("%d",&n)
#define Scand2(a,b) scanf("%d%d",&a,&b)
#define Scand3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s) scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 2e5 + 50;
const int INF = 0xffffff;
#ifndef ONLINE_JUDGE
#endif // ONLINE_JUDGE
inline int read(){
int sgn = 1; int sum = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if(ch == '-') sgn = -sgn;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
sum = sum*10+(ch-'0');
ch = getchar();
}
return sgn*sum;
}
int arr[maxn];
ll n,s;
int main()
{
#ifndef ONLINE_JUDGE
//Fin;
#endif // ONLINE_JUDGE
n = read(); s = read();
fo(i, 0, n)
arr[i] = read();
sort(arr, arr+n);
ll mid = n>>1;
ll ans = 0;
ll ind = 0;
if(arr[mid] == s){
printf("%lld\n",ans);
return 0;
}else if(arr[mid] < s){
ans += s-arr[mid];
ind = mid+1;
while (arr[ind] < s && ind < n) { //要对ind判断是否越界
ans += s-arr[ind];
ind++;
}
}else if(arr[mid] > s && ind >= 0){ //要对ind判断是否越界
ans += arr[mid]-s;
ind = mid-1;
while (arr[ind] > s) {
ans += arr[ind]-s;
ind--;
}
}
printf("%lld\n",ans);
return 0;
}
C
这道题也很容易想到解法,第一个操作(交换操作)当且仅当i和j相邻,才会执行,否则就执行第二个操作,这是最优策略。
也是很快就写好代码了,交了一发,还是wa。。。。难受啊。。。
仔细分析,这个策略绝对是最优策略,不可能出错,那么就是代码问题了。于是又是检查代码,发现在判断执行第一个操作是,判断条件错了,我写的是if(arr1[ind] != arr2[ind] && ind+1 < n && arr1[ind+1] != arr2[ind+1]),这是不对的,当arr1[ind]=arr1[ind+1]时,是不需要交换的,改成if(arr1[ind] != arr2[ind] && ind+1 < n && arr1[ind+1] != arr2[ind+1] && arr1[ind] != arr1[ind+1])就过了。
错误原因:
题目理解正确,所想也正确,数据类型范围均合适,代码速度达已最优,程序正常执行(未越界,未除零),所写却非所想。
解决方式:
仔细读代码,寻找隐藏bug。
代码:
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin freopen("in.txt","r",stdin)
#define Fout freopen("out.txt","w",stdout)
#define Case(T) int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b) for(int i = a; i < b; ++i)
#define fd(i,a,b) for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val) fill(a,a+n,val)
#define Scand(n) scanf("%d",&n)
#define Scand2(a,b) scanf("%d%d",&a,&b)
#define Scand3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s) scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 1e6 + 50;
const int INF = 0xffffff;
#ifndef ONLINE_JUDGE
#endif // ONLINE_JUDGE
inline int read(){
int sgn = 1; int sum = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if(ch == '-') sgn = -sgn;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
sum = sum*10+(ch-'0');
ch = getchar();
}
return sgn*sum;
}
int n;
char arr1[maxn], arr2[maxn];
int main()
{
#ifndef ONLINE_JUDGE
//Fin;
#endif // ONLINE_JUDGE
n = read();
int ans = 0;
fo(i, 0, n)
scanf("%c",&arr1[i]);
getchar();
fo(i, 0, n)
scanf("%c",&arr2[i]);
int ind = 0;
while (ind < n) {
if(arr1[ind] != arr2[ind] && ind+1 < n && arr1[ind+1] != arr2[ind+1] && arr1[ind] != arr1[ind+1]){
ans++;
arr1[ind] = arr2[ind]; arr1[ind+1] = arr2[ind+1];
ind += 2;
}else if(arr1[ind] != arr2[ind]){
ans++;
ind++;
}else{
ind++;
}
}
printf("%d\n",ans);
return 0;
}
D
这题题意很简单,就是要求你写一个bfs,然后判断能否输出规定的序列。
第一次尝试cf的D题(我菜啊。。。),这次的D题还是可以写的,直接用bfs模拟就行,依次遍历要输出的序列中的字母,在当前顶点的邻接点中find,若是找到,就继续找下一个字母,弱若是找不到,就在队列中下一个顶点中的邻接点中find,最后判断一下,是否完全输出了规定序列就行。
一开始一直wa在第39个样例,一直找不到wa点,后来仔细读题才发现,题目有一行字加粗了。。。要求序列必须从1开始。。。我这是睁眼瞎啊。。。
改了之后,又交了一发,结果发现T在第48个,心想是自己的算法不对吗,仔细分析了自己的算法,发现并不应该超时啊,仔细看了代码,发现在查找时,用的是algothrim中的find函数,之前一直以为库函数中的函数复杂度都挺好的,那么这个find函数怎么说都应该达到O(logn)吧,结果并没有。。。这个函数的复杂度是O(n)的,心想也对,要达到O(logn),应该要用二分,那么二分的前提是有序,作为适用所有结构的“通用find函数",自然不会采用二分,那么如果用了O(n)的find,超时那是肯定的,所以得用logn的find函数,而这样的find函数,只能调用map、set容器自带的find函数,st.find(x)。
错误原因:
①题意没看清,忽略了一些特定的条件
②超时的话,不一定算法就不行,可能是有些部分、有些函数不是很优化。
代码:
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin freopen("in.txt","r",stdin)
#define Fout freopen("out.txt","w",stdout)
#define Case(T) int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b) for(int i = a; i < b; ++i)
#define fd(i,a,b) for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val) fill(a,a+n,val)
#define Scand(n) scanf("%d",&n)
#define Scand2(a,b) scanf("%d%d",&a,&b)
#define Scand3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s) scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 2e6 + 50;
const int INF = 0xffffff;
#ifndef ONLINE_JUDGE
#endif // ONLINE_JUDGE
inline int read(){
int sgn = 1; int sum = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if(ch == '-') sgn = -sgn;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
sum = sum*10+(ch-'0');
ch = getchar();
}
return sgn*sum;
}
set<int> mp[maxn];
int arr[maxn], vis[maxn];
int n;
int success;
queue<int> que;
void bfs(){
int ind = 1;
int startt = arr[0];
while (!que.empty()) {
que.pop();
}
que.push(startt);
vis[startt] = 1;
while (!que.empty()) {
int front = que.front();
que.pop();
while (1) {
int aim = arr[ind];
if(mp[front].find(aim) == mp[front].end())
break;
if(!vis[aim]){
vis[aim] = 1;
que.push(aim);
ind++;
if(ind == n){
success = 1;
return;
}
}else{
success = 0;
return;
}
}
}
if(ind < n)
success = 0;
else
success = 1;
return ;
}
int main()
{
#ifndef ONLINE_JUDGE
//Fin;
#endif // ONLINE_JUDGE
n = read();
fo(i, 0, n-1){
int u = read(); int v = read();
mp[u].insert(v);
mp[v].insert(u);
}
fo(i, 0, n){
arr[i] = read();
}
if(arr[0] != 1){
printf("No\n");
return 0;
}
fi(vis, maxn-5, 0); success = 0;
bfs();
if(success)
printf("Yes\n");
else
printf("No\n");
return 0;
}
通过上面四题,总结了AC的正确打开方式:
①读懂题意,不能忽略一些细节要求
②选择合适的数据类型以及合适的存储范围
③选择合适的算法,如果超时了,也不要全盘否定,可能还可以优化
④做到所写即所想,如果wa了,查查代码是否实现了自己所想的功能。查查是否越界啥的,或者”=“与”==“问题,初始化,除零等问题,还是wa的话,就得想想自己的算法是否正确了。
另外可以参看:我写的debug总结:https://blog.csdn.net/vaeloverforever/article/details/81783932