A.Triangle
一到四题都比较简单,就直接贴代码了
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 4;
int length[maxn];
bool triangle(int no){
int i, j = 0, a[3];
for(i = 0; i < 4; i++)
if(i != no) a[j++] = length[i];
for(i = 0; i < 3; i++)
if(a[i] == 0) return false;
sort(a,a+3);
if(a[0] + a[1] > a[2]) return true;
else return false;
}
bool segment(int no){
int i, j = 0, a[3];
for(i = 0; i < 4; i++)
if(i != no) a[j++] = length[i];
for(i = 0; i < 3; i++)
if(a[i] == 0) return true;
sort(a,a+3);
if(a[0] + a[1] == a[2]) return true;
else return false;
}
int main(){
int i, j, k, n = 4;
for(i = 0; i < n; i++)
scanf("%d",length+i);
int ok = 0;
for(i = 0; i < n; i++)
if(triangle(i)){
ok = 1;
break;
}
if(ok){
printf("TRIANGLE\n");
return 0;
}
ok = 0;
for(i = 0; i < n; i++)
if(segment(i)){
ok = 1;
break;
}
if(ok){
printf("SEGMENT\n");
return 0;
}
printf("IMPOSSIBLE\n");
return 0;
}
B. President's Office
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const int maxn = 120;
const int dx[] = {0,0,-1,+1};
const int dy[] = {-1,+1,0,0};
char r;
int n, m;
int color[1000];
char matrix[maxn][maxn];
char get(){
char c = getchar();
while((c < 'A' || c > 'Z') && c != '.')
c = getchar();
return c;
}
int main(){
int i, j, k;
scanf("%d%d",&n,&m);
r = get();
memset(color,0,sizeof(color));
for(i = 0; i < n; i++)
for(j = 0; j < m; j++)
matrix[i][j] = get();
color[r] = 1;
color['.'] = 1;
for(i = 0; i < n; i++)
for(j = 0; j < m; j++)
if(matrix[i][j] == r){
for(k = 0; k < 4; k++){
int u = i + dx[k];
int v = j + dy[k];
if(u >= 0 && u < n)
if(v >= 0 && v < m){
color[matrix[u][v]]++;
/* while(matrix[u][v] == '.'){
u = u + dx[k];
v = v + dy[k];
}
*/
}
}
}
int ans = 0;
for(i = 0; i < 1000; i++)
if(color[i])
ans++;
printf("%d\n",ans-2);
return 0;
}
C. Alice, Bob and Chocolate
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const int maxn = 120000;
int t[maxn], sum[maxn];
int main(){
int i, j, n, a, b;
scanf("%d",&n);
for(i = 0; i < n; i++)
scanf("%d",t+i);
int left, right;
left = right = 0;
a = -1, b = n;
while(a+1 != b){
if(left <= right){
left += t[++a];
}else{
right += t[--b];
}
//printf("%d %d\n",left,right);
}
printf("%d %d\n",a+1, n-b);
return 0;
}
D. Lizards and Basements 2
这题可以用dp解决的,不过当初图方便写了搜索,其实写得比dp长好多
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const int maxn = 1000;
int n, a, b;
int h[maxn];
int cur[maxn];
int now[maxn];
int ans = maxn;
bool dfs(int who, int num){
int i, j;
if(num >= ans) return false;
if(who == n){
if(h[n] >= 0) return false;
ans = num;
for(i = 2; i < n; i++)
now[i] = cur[i];
return true;
}else{
int begin = ((h[who-1]+b-1)/b);
if(begin < 0) begin = 0;
for(j = begin; j <= 16; j++)
if(h[who -1] - b * j >= 0) continue;
else {
if(j + num >= ans) break;
if(who == n - 1 && h[who+1] - b*j >= 0) continue;
cur[who] = j;
h[who-1] -= b * j;
h[who] -= a * j;
h[who+1] -= b * j;
bool ok = dfs(who+1,num+j);
h[who-1] += b * j;
h[who] += a * j;
h[who+1] += b * j;
if(h[who-1] - b*j < 0 && h[who+1] - b*j < 0 && h[who] - a*j < 0) break;
}
}
return false;
}
int main(){
int i, j, sum = 0;
scanf("%d%d%d",&n,&a,&b);
for(i = 1; i <= n; i++)
scanf("%d",h+i);
dfs(2,0);
printf("%d\n",ans);
for(i = 1; i <= n; i++)
for(j = 1; j <= now[i]; j++){
sum++;
if(sum == ans) printf("%d\n",i);
else printf("%d ",i);
}
return 0;
}
E. Exposition
第五题 我要是再直接贴代码,这遍文章就没法看了。所以,第五题会讲得很长(如果有时间 ,我会把前面的详细题解也补一下的)
题意:给定一个序列,求序列中最长的连续的数,并且其中,最大和最小的差不超过给定的k
输入:第一行:n,k(1<=n<=10^5,0<=k<=10^6)分别表示序列的长度,和最在的差值。
第二行:给定的序列
输出:第一行:两个数分别表示最大的长度,和有几个这样的区间
以下每行两个数,分别代表所求的区间
题解:首先,我们可以知道如果区间 [ l, r+1 ] 满足条件,那么区间 [l,r] 肯定满足条件。同样的如果区间[ l-1, r]满足条件,那么 [l,r]也满足条件。所以,如果固定起点,或者固定终点,则最大的长度满足单调性。所以我们有以下的方法。
方法一:
枚举起点,二分终点,或者说是二分区间的长度。然后,求最大和最小值看是否满足条件。要在固定的区间内求最大最小值,就是著名的RMQ问题,这边可以用线段树解决,我这边用的是经典的ST(Space Table)来解决。
这边我们算一下复杂度。ST算法预处理需要O(nlogn)的复杂度,而每次询问需要O(1)的复杂度。枚举起点每次需要O(log n)的复杂度,要枚举n次,复杂度也是O(n log n),固总的复杂度为O(nlong)(如果用的是线段数来解决RMQ则复杂度为O(n *log n*log n )会多个log n 的时间,实际的话,需要 800多ms),cf上测试的时间为 400多ms.
代码:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const int maxk = 25;
const int maxn = 130000;
int n, k;
int h[maxn];
int min[maxn][maxk];
int max[maxn][maxk];
bool MIN(int a, int b){
return (a < b);
}
bool MAX(int a, int b){
return (a > b);
}
int getMin(int a, int b){
if(a < b) return a;
else return b;
}
int getMax(int a, int b){
if(a > b) return a;
else return b;
}
void build(int dp[][maxk],bool func(int,int)){
int i, j, k;
for(i = 1; i <= n; i++)
dp[i][0] = i;
for(k = 1,j = 1; k*2 <= n; k *= 2,j++)
for(i = 1; i+k*2-1 <= n; i++)
if(func(h[dp[i][j-1]],h[dp[i+k][j-1]]))
dp[i][j] = dp[i][j-1];
else
dp[i][j] = dp[i+k][j-1];
}
int query(int l, int r){
int len = r - l + 1;
int maxValue, minValue;
int t = (int)(log(len*1.0)/log(2.0));
maxValue = getMax(h[max[l][t]],h[max[r + 1 - (1 << t)][t]]);
minValue = getMin(h[min[l][t]],h[min[r + 1 - (1 << t)][t]]);
return (maxValue - minValue);
}
int get(int i){
int l, r;
l = i, r = n + 1;
while(l < r - 1){
int m = (l + r) >> 1;
if(query(i,m) <= k) l = m;
else r = m;
}
return (l - i + 1);
}
int main(){
int i, j;
scanf("%d%d",&n,&k);
for(i = 1; i <= n; i++)
scanf("%d",h+i);
build(min,MIN);
build(max,MAX);
int que[maxn];
int now, top, ans = 0;
for(i = 1; i <= n; i++){
now = get(i);
if(ans < now) {
ans = now;
top = 0;
que[top++] = i;
}else if(ans == now){
que[top++] = i;
}
}
printf("%d %d\n",ans,top);
for(i = 0; i < top; i++)
printf("%d %d\n",que[i],que[i]+ans-1);
return 0;
}
实际上,上述的算法,写得又长,效率又低。这题实际上有更好的方法
方法二:
如果我们知道以i开头的连续序列长度可以达到 len , 那么,以i+1开头的长度至少为 len - 1。那么我们可以在i,的基础上,来测试 以 i+1 开头的长度为 len的是否可以,这样一直做下去,直到找到 一个不符合条件的,那么此时序列的长度减一,就是以i开头可以得到的最大可以符合条件的最大长度。那么,这边就有一个问题了,我们如何知道一个区间的最大和最小值?我们可以先假定,我们只要求最大值(最小值可以类推)。我们可以用一个叫做单调队列的结构来储存求这个值。单调队列是这样做的,我们将一个新的数入队,如果发现现在队尾的元素没有比我们入队的大,那我们就可以把这个数出队。如果,我们发现最大的数就是现在 我元素i,那么我们在i做完处理后,我们就可以把i出队。之所以可以这样做是因为:如果队中的数比新加入的数大,那么它就不可能是以i,开头甚至以后的区间中,最大的元素(因为我们新入队的数永远比它大),也就是这个数是没有价值的,那么我们就可以抛弃它。如果最大的数是元素i,那么它对i+1及以后的结果是没影响的,所以我们也可以且必须抛弃它。
这里我们算一下复杂度。由于在单调队列中,每个元素只入队一次,出队一次,固总的复杂度为O(n),已经达到理论的上界。实际运行中只用了80ms左右。
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const int maxn = 130000;
int h[maxn], len[maxn];
int min[maxn], max[maxn];
int rank1[maxn], rank2[maxn];
int minl, minr, maxl, maxr;
int main(){
int i, j, n, k;
scanf("%d%d",&n,&k);
for(i = 0; i < n; i++)
scanf("%d",h+i);
h[n] = h[n-1] + k + 1;
minl = minr = 0;
maxl = maxr = 0;
rank1[minr] = rank2[maxr] = 0;
min[minr++] = max[maxr++] = h[0];
for(i = 0, j = 1; i < n; i++){
for(;max[maxl] - min[minl] <= k;j++){
while(minl != minr && min[minr-1] >= h[j])
minr--;
min[minr] = h[j],rank1[minr] = j;
minr += 1;
while(maxl != maxr && max[maxr-1] <= h[j])
maxr--;
max[maxr] = h[j], rank2[maxr] = j;
maxr += 1;
}
len[i] = j - i - 1;
if(rank1[minl] == i) minl++;
if(rank2[maxl] == i) maxl++;
}
int ans = -1, num = 0;
for(i = 0; i < n; i++)
if(len[i] > ans){
num = 1;
ans = len[i];
}else if(len[i] == ans){
num++;
}
printf("%d %d\n",ans,num);
for(i = 0; i < n; i++)
if(len[i] == ans)
printf("%d %d\n",i+1,i+len[i]);
return 0;
}