A. 寻找最长合法括号序列II
和第一次的比赛类似,栈的应用,模拟题。首先得分析出,最长合法括号序列肯定是独立存在的,不存在两个最长合法括号序列重叠的情况,若重叠只能得到一个更长的序列。那么,我们需要一个辅助数组记录原序列当中哪些括号是被匹配过的,记1。然后再线扫这个辅助数组中1的个数,就是答案。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<string>
#include<algorithm>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#define N 1000005
using namespace std;
char str[N],sub[N];
stack<int> s;
inline int match(int a,int b)
{
if(str[a]=='('&&str[b]==')')
return 1;
return 0;
}
int main()
{
int len,i,j,l,start,end;
while(scanf("%s", str) != EOF)
{
len = strlen(str);
memset(sub,0,sizeof(sub));
while(!s.empty()){
s.pop();
}
s.push(0);
for(i=1;i<len;i++)//序号入栈
{//匹配过程
if(s.empty()) s.push(i);
else if(match(s.top(),i)){
sub[s.top()]=1;
sub[i]=1;
s.pop();
}
else
s.push(i);
}
int ans = 0;
for(i = 0; i < len; ++i)
{
if(sub[i] == 1)
ans++;
}
printf("%d\n",ans);
}
return 0;
}
B. 城际公路网
最短路变形。记得在最短路floyd算法当中,是否常有这样一段代码,min[i][k]+min[k][j]<min[i][j]
。min[i][i]表示从起点i到终点j的最短路径,这段代码其实在枚举中间点k的过程。那么现在,我们是改变了一条边,试想能否从必定经过这条边的路径上入手。得min[i][a]+min[b][j]+map[a][b]<min[i][j].这样,中间枚举的k时间少了,成了固定的map[a][b],因此复杂度降到(n^2)。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<string>
#include<algorithm>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
#define MAXN 300
#define inf 1000000000
typedef int elem_t;
void floyd_warshall(int n, elem_t min[][MAXN], int a, int b, int c){
//直接修改了原图,保证图内所有点之间都是最短的。
int i,j,k;
for (i=0;i<n;i++){
for (j=i+1;j<n;j++){
if (min[i][a]+min[b][j]+c<min[i][j]){
min[j][i]=min[i][j]=min[i][a]+min[b][j]+c;
}
if (min[i][b]+min[a][j]+c<min[i][j]){
min[j][i]=min[i][j]=min[i][b]+min[a][j]+c;
}
}
}
return ;
}
int mat[MAXN][MAXN];
int main()
{
int n;
while(scanf("%d", &n) != EOF){
int i, j;
int ans = 0;//记录当前的所有最短路的和
for(i = 0; i < n; ++i){//输入地图
for(j = 0; j < n; ++j){
scanf("%d", &mat[i][j]);
}
}
for(i = 0; i < n; ++i){
for(j = i + 1; j < n; ++j){
ans = ans + mat[i][j];
}
}
int k;
scanf("%d", &k);
while(k--){
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
a--;
b--;
if(mat[a][b] > c){//剪枝,若改变的路径比原图大,那么可以不考虑,直接使用上次的答案。
mat[a][b] = mat[b][a] = c;
floyd_warshall(n,mat,a,b,c);
ans = 0;
for(i = 0; i < n; ++i){
for(j = i + 1; j < n; ++j){
ans = ans + mat[i][j];
}
}
}
printf("%d\n", ans);
}
}
return 0;
}
C.
可乐瓶展览
题目意思很直白,就是要求最长的序列,且序列的最大和最小之间的差不能超过k,问这样的最长的序列有多少个。
可以用一个容器,里面存放当前序列的有序序列。用一个游标,记录当前的开始位置,线扫结束位置。若容器内的最大和最小的值超过了k,则开始游标向后移动,去掉容器内开始游标所指向的值。那么这个容器可以用一个二叉平衡树。
#include <iostream>
#include <set>
#include <vector>
#include <cstdio>
using namespace std;
#define MAXN 100010
int h[MAXN];
multiset<int> s;//所指的容器,其实是一个树
struct node{//最后的答案
int x, y;
}result[MAXN];
int id;
inline bool scan_d(int &num) //输入外挂,对大数据有比较好的效果。
{
char in;bool IsN=false;
in=getchar();
if(in==EOF) return false;
while(in!='-'&&(in<'0'||in>'9')) in=getchar();
if(in=='-')
{
IsN=true;num=0;
}
else num=in-'0';
while(in=getchar(),in>='0'&&in<='9')
{
num*=10,num+=in-'0';
}
if(IsN) num=-num;
return true;
}
int main()
{
int n, k, head;
int max_size = 0;
int n_count = 0;
while (scanf("%d%d", &n, &k) != EOF) {
head = 0, n_count = 0, s.clear(), max_size = 0;
id = 0;
int i;
for(i = 0; i < n; ++i){
scan_d(h[i]);
}
for (i = 0; i < n; ++i) {
s.insert(h[i]);
while (*(s.rbegin()) - *(s.begin()) > k)//最大和最小的比较
s.erase(s.find(h[head++]));
if(s.size() + n - i + 1 < max_size)//剪枝,若容器内的元素个数加上当前剩余的元素个数无法达到目前的最大值,退出
break;
if (s.size() > max_size) {
max_size = s.size();
id = 1;
result[0].x = head;
result[0].y = i;
}
else if (s.size() == max_size) {
result[id].x = head;
result[id].y = i;
++id;
}
}
printf("%d %d\n", max_size, id);
for (int i = 0; i < id; i++)
printf("%d %d\n", result[i].x + 1, result[i].y + 1);
}
return 0;
}