codeforces --- Round #244 (Div. 2) B. Prison Transfer
The prison of your city has n prisoners. As the prison can't accommodate all of them, the city mayor has decided to transfer c of the prisoners to a prison located in another city.
For this reason, he made the n prisoners to stand in a line, with a number written on their chests. The number is the severity of the crime he/she has committed. The greater the number, the more severe his/her crime was.
Then, the mayor told you to choose the c prisoners, who will be transferred to the other prison. He also imposed two conditions. They are,
- The chosen c prisoners has to form a contiguous segment of prisoners.
- Any of the chosen prisoner's crime level should not be greater then t. Because, that will make the prisoner a severe criminal and the mayor doesn't want to take the risk of his running away during the transfer.
Find the number of ways you can choose the c prisoners.
The first line of input will contain three space separated integers n (1 ≤ n ≤ 2·105), t (0 ≤ t ≤ 109) and c (1 ≤ c ≤ n). The next line will contain n space separated integers, the ith integer is the severity ith prisoner's crime. The value of crime severities will be non-negative and will not exceed 109.
Print a single integer — the number of ways you can choose the c prisoners.
4 3 3
2 3 1 1
2
1 1 1
2
0
11 4 2
2 2 0 7 3 2 2 4 9 1 4
6
【题目链接】
http://codeforces.com/contest/427/problem/B
【题目大意】
某市的监狱犯人关满了,需要从n个囚徒中选择c个转移至另外一个监狱,监狱长给出了选择的两个要求,让你计算有多少种方法然后输出方法数。
要求:
1.所选的囚徒必须都是连续的;
2.所选的囚徒中,每个囚徒的犯罪严重程度不超过t。
【题目分析】
首先看懂题后,心中大喜,这么简单的题,二话不说就写代码,当时都没多考虑,写完测试数据都没测就提交了,OK,初判过了,然后就做下一题,最后可想而知,超时了。
还是太粗心了,时间复杂度都没分析。附上比赛时提交的代码:
比赛代码(超时):
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int main() { int n,t,c; int i,j,k; int flag; int cnt=0; scanf("%d%d%d",&n,&t,&c); int a[200010]; for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=c;i<=n;i++) { flag=0; for(j=0;j<c;j++) { if(a[i-j]>t) { flag=1; break; } } if(flag==0) cnt++; } printf("%d\n",cnt); return 0; }
好吧,比赛结束了,rating也掉了,心也上得差不多了,那就再来重新总结一下,再做一遍。
既然是超时,那就优化呗。
一开始我想到了上面的代码每次比较的时候基本上都要c次的回溯,时间浪费的太多了,这就好比字符串的模式匹配的BF算法一样,那何不将BF算法像KMP一样优化一下呢(扯远了),不需要每次都回溯,每次判断的时候如果遇到了一个大于t的数,那么接下来的c个都不能和他去新监狱里愉快的玩耍了,所以接下来的这c个根本就没有回溯的必要,所以又有了一下的SB代码:
代码(仍然超时):
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int main() { int n,t,c; int i,j,k; int flag; int cnt=0; scanf("%d%d%d",&n,&t,&c); int a[200010]; for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=c;i<=n;) { // k=i; flag=0; for(j=0;j<c;j++) { if(a[i-j]>t) { flag=1; i+=c; break; } } if(flag==0) {cnt++;i++; // cout<<k<<" "<<a[k]<<endl; } } printf("%d\n",cnt); return 0; }
提交后一看,还超时,好吧,继续优化,我又想到其实如果判断了k个后,如果这k个都是小于t的(以下简称合法的),那么我就不用回溯了,直接判断当前这个数是否合法,合法那么方法就加1,遇到不和法的数就再次开始直到连续合法数达到c,那就可以继续以上的统计了,我就用一个变量full来统计连续合法数的个数。
代码(超时):
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int main() { int n,t,c; int i,j,k; int flag; int cnt=0; int full=0; scanf("%d%d%d",&n,&t,&c); int a[200010]; for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=c;i<=n;) { // k=i; flag=0; if(full>=c) { if(a[i]<=t) { cnt++; i++; continue; } else { flag=1; i+=c; full=0; continue; } } else { for(j=0;j<c;j++) { if(a[i-j]>t) { flag=1; i+=c; full=0; break; } else full++; } } if(flag==0) {cnt++;i++; // cout<<k<<" "<<a[k]<<endl; } } printf("%d\n",cnt); return 0; }
提交一看,What?还超时,我不得不怀疑我的方法的正确性了,在吃饭的路上我独自想了一下,突然发现这就是一个简单的区间统计吗?只用扫描一遍统计出每一段数值都不超过t的区间有多少个不就可以了吗。
AC代码:
#include<iostream> using namespace std; int main() { int n,t,c; int i,j,k; int ans=0; int cnt=0; cin>>n>>t>>c; for(i=0;i<n;++i) { cin>>k; if(k>t) { if(cnt>=c) ans+=cnt-c+1; cnt=0; } else cnt++; } if(cnt>=c) //对最后一个cnt进行判断 ans+=cnt-c+1; cout<<ans<<endl; return 0; }
经过了一道水题的这般周折以后,我还是悟到了一些东西,codeforces div.2中的比赛一般是考理解英语题目的能力和解决一些小问题的能力,算法一般考的很少,但是这些方法都很巧妙,所以还是能锻炼编程能力的。