寒假集训第一天!!欧耶!!>_<!!
好久前就听说过STL,这不今天刚学,还是热乎的嘞
于是乎,刷了几道可能有关的题
老规矩,先上知识点
队列(先进先出--FIFO)
创建队列:queue<typename> q;
添加元素:q.push(元素名)
去掉队首:q.pop()
访问队首:q.front()
访问队尾:q.back()
判断是否为空:q.empty()
返回队列大小:q.size()
优先队列(本质其实是堆)
创建队列:priority_queue<typename> q
添加元素:q.push(元素名)
删除队首元素:q.pop()
判断为空:q.empty()
返回队列大小:q.size()
访问最优元素:q.top()
对于 int 型的优先队列,默认为大顶堆,即元素按降序排列,对此可进行重载:
struct T
{
int z;
}t;
bool operator<(const T t)const //bool opertao<(const T &t1,const T &t2)const
{
return z<t.z; //return t1.z<t2.z;
}
当然对于具体情况当然需要具体分析
栈
栈有三个特点:
1、先进后出
2、在栈顶加入元素
3、在栈顶删除元素
操作如下:
创建队列:stack<typename> s;
栈顶添加元素:s.push(元素名)
删除栈顶元素:s.pop()
访问栈顶元素:s.top()
判断是否为空:s.empty()
返回栈的大小:s.size()
由于栈和队列没有clear函数因此对于清空某个队列或栈,需要循环遍历来清除:
while(!s.empty())
s.pop();
向量(动态数组)
1、定义:vector<int> a;
2、初始化:
(1)vector<int> a;//初始化size为0
(2)vector<int> a(10);//初始化10个默认值为0的元素
(3)vector<int> a(10,1);//初始化10个值为1的元素
3、基本操作
取首元素地址:a.begin()
取尾元素的下一个地址:a.end()
首元素的值:a.front()
尾元素的值:a.back()
下标形式可访问:a[10]
尾部添加元素:a.push_back(元素名)
删除尾部首元素:a.pop_back()
判断为空:a.empty()
返回元素个数:a.size()
翻转向量:reverse(a.begin(),a.end());
4、经典应用:邻接表(常用于存图)
struct edge
{
int from,to,value;
}
vector<edge> Map[10005]
集合
set是个有序的容器,可对其进行插入、删除、查找等操作
创建集合:set<int> st
清空集合:st.clear
插入元素:st.insert(x) //具有互异性
查询是否有x:s.count(x) //返回0或1
查找x并返回迭代器:set<int>::iterator it=s.find(x)
判断空集:s.empty()
返回元素个数:s.size()
删除元素x:s.erase(x)
集合的最主要用途:自动去重并升序排序
set只可用迭代器进行访问!!!
遍历:
for(set<int>::iteartor it=st.begin();it!=st.end();it++)
cout<<*it<<endl;
改变对set次序排列的标准:若set中元素为结构体类型,则在结构体中重载运算符即可
string类
字符串str长度:str.length(),str.size()
链接str1和str2:str1+=str2;
比较:比较运算符使用“ < , <= , == , != , >= , >”
求子串s1:s2=s1.substr(n,m) //从s2下表为n到下标为m的部分
(若m省略或者m>s2.length()则为s2整个字符串)
插入:s1.insert(n,s2) //在下标n处插入s2
s1.insert(n,m,s2) //在下标n处插入m个s2
删除:s1.erase(n,m) //从下标为n的元素删除到下标m元素
交换:s1.swap(s2)
查找:pos1=str.find(key) //找key在str第一次出现的位置
pos2=str.find(key,pos) //找在str中区间 [ pos,end ]中key第一次出现的位置
排序:sort(str.begin(),str.end())
翻转:reverse(str.begin(),str.end())
映射
创建映射:map<typename1,typename2> m;
判断m中键值为k的元素是否存在:m.count(k); //返回1或0
查找k在m中位置:m.find(k) //存在则返回迭代器,不存在则返回end()
删除键值为k的元素,返回删除k的个数:m.erase(k)
删除迭代器p指向的元素:m.erase(p)
添加用于m上的键值e(pair),若m已有键值,则不进行操作,若e.first不在m中,则插入作为e.second():m.insert(e)
清空:m.clear()
判断是否为空:m.empty()
再来个函数:
next_permutation()&&prev_permutation
前者:生成下一个全排列--到最后一个返回false
后者:生成上一个全排列
next_permutation(s.begin(),s.end());//迭代器
next_permutation(s,s+n);//数组下标
下面是今天做的题目:
P1629 邮递员送信
本题在dijistra的板子基础上弄了一些改动,因为邮递员送快递的时候需要回去,而回去的路也存在最小值,因此在存边的时候还需要来个反向存边,然后啥都翻过来,最后把两遍行走最小路加起来就OK
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
struct Edge
{
int to;
int next;
int w;
}edge[N],fedge[N];
int head[N];
int fhead[N];
int tim[N];
int ftim[N];
bool vis[N];
bool fvis[N];
int cnt=1;
int sum;
int n,m;
void add(int u,int v,int w)
{
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
fedge[cnt].to=u;
fedge[cnt].w=w;
fedge[cnt].next=fhead[v];
fhead[v]=cnt;
cnt++;
}
struct dl
{
int v,dis;
bool operator<(const dl &p)const
{
return p.dis<dis;
}
};
priority_queue<dl> q;
priority_queue<dl> fq;
void dij(void)
{
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
tim[i]=0x7fffffff;
tim[1]=0;
q.push((dl){1,0});
while(!q.empty())
{
dl k=q.top();
q.pop();
int v=k.v;
if(vis[v])
continue;
vis[v]=true;
for(int i=head[v];i;i=edge[i].next)
{
int ch=edge[i].to;
if(tim[v]+edge[i].w<tim[ch])
{
tim[ch]=tim[v]+edge[i].w;
q.push((dl){ch,tim[ch]});
}
}
}
}
void fdij(void)
{
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
ftim[i]=0x7fffffff;
ftim[1]=0;
fq.push((dl){1,0});
while(!fq.empty())
{
dl k=fq.top();
fq.pop();
int v=k.v;
if(vis[v])
continue;
vis[v]=true;
for(int i=fhead[v];i;i=fedge[i].next)
{
int ch=fedge[i].to;
if(ftim[v]+fedge[i].w<ftim[ch])
{
ftim[ch]=ftim[v]+fedge[i].w;
fq.push((dl){ch,ftim[ch]});
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v,tim;
cin>>u>>v>>tim;
add(u,v,tim);
}
dij();
fdij();
for(int i=2;i<=n;i++)
sum+=tim[i]+ftim[i];
cout<<sum<<endl;
return 0;
}
P5250 【深基17.例5】木材仓库
这题第一反应不就是个set么。。。
然后对set进行基操就行
代码:
#include<bits/stdc++.h>
using namespace std;
int n,op,t;
set<int>::iterator l,l2,l3;
set<int> s;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>op>>t;
if(op==1)
{
if(!s.insert(t).second)
cout<<"Already Exist"<<endl;
}
else
{
if(s.empty())
{
cout<<"Empty"<<endl;
continue;
}
if(s.find(t)!=s.end())
cout<<t,s.erase(s.find(t));
else
{
l=l2=l3=s.lower_bound(t);
if(l==s.begin())
cout<<*l,s.erase(l);
else if(l==s.end())
cout<<*(--l3),s.erase(l3);
else if(*l-t<t-*(--l2))
cout<<*(l3),s.erase(l3);
else
cout<<*(--l3),s.erase(l3);
}
cout<<endl;
}
}
}
P1323 删数问题
这题在添加元素的时候需要升序排列,因此用上了优先队列,然后就是简简单单的删数问题了,需要求最大数,那么一旦发现左边数比右边小,就删掉左边的数
代码:
#include<bits/stdc++.h>
using namespace std;
int k,m;
string s;
const int N=3e4+5;
string str;
priority_queue<int,vector<int>,greater<int> >q;
string chstring(int x)
{
int k=x;
str="";
while(k!=0)
{
str+=char(k%10)+'0';
k/=10;
}
reverse(str.begin(),str.end());
return str;
}
int main()
{
cin>>k>>m;
q.push(1);
for(int i=1;i<=k;i++)
{
int p=q.top();
q.pop();
s+=chstring(p);
q.push(2*p+1);
q.push(4*p+5);
}
cout<<s<<endl;
int cnt=0;
while(1)
{
for(int i=0;i<s.length()-1;i++)
{
if(s[i]<s[i+1])
{
cnt++;
s.erase(i,1);
if(cnt>=m)
{
cout<<s<<endl;
return 0;
}
break;
}
}
}
return 0;
}
P1169 [ZJOI2007] 棋盘制作
本题是一道dp,求矩阵的最大子矩阵问题--------典型极了
肯定不能蛮做,那么这就用到了一种高级方法::悬线法(蒟蒻只能说这tql)
悬线法:
1、构造悬线
在初始状态下,每一个元素都是一根悬线,假设其位置为matrix[ i ] [ j ](第i 行第j jj列),
它具有三个属性,悬线的高度 height[i][j] ,悬线向左能到达的最远的合法列 left[i][j] 以及 悬线向右能达到的最远的合法列 right[i][j] ,所以在初始条件下height [ i ] [ j ] = 1, left [ i ] [ j ] = right[ i ][ j ]=j。
不难发现,有如下状态转移方程,固定一行时(因为目前悬线的长度为1),从左向右枚举如果( i , j ) 和 ( i , j − 1 ) 两个点互相合法(在此例中是不相等), left[ i ] [ j ] = left [ i ] [ j-1 ] ,同理从右向左枚举时,如果( i , j ) 和 ( i , j − 1 ) 两个点互相合法,则有right [ i ] [ j ] = right [ i ] [ j+1 ] ,至此,所有高度为1的悬线构造完毕。
2、连接悬线
拼接悬线时我们应该纵向观察,首先要求上下两个元素相互合法,应该取这两根悬线左右移动时都能达到的列,故需要对lef和righ持续更新并一步步转移,即实现dp
if(i>1&&point[i][j]!=point[i-1][j])
{
lef[i][j]=max(lef[i][j],lef[i-1][j]);
righ[i][j]=min(righ[i][j],righ[i-1][j]);
height[i][j]=height[i-1][j]+1;
}
3、求面积
对每个点的lef和righ作差+1,所得值即为该点左右能到的最远列,即为矩形的一条边,乘以高度height则得到矩形最小面积。不难发现,最小正方形一定在最小矩形内,故其边为最大矩形的边的最小值
int x=righ[i][j]-lef[i][j]+1;
int y=min(x,height[i][j]);
maxx1=max(maxx1,y*y);
maxx2=max(maxx2,x*height[i][j]);
最终代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=2e3+5;
int lef[N][N];
int righ[N][N];
int height[N][N];
int point[N][N];
int maxx1,maxx2;
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>point[i][j];
lef[i][j]=j;
righ[i][j]=j;
height[i][j]=1;
}
for(int i=1;i<=n;i++)
for(int j=2;j<=m;j++)
if(point[i][j]!=point[i][j-1])
lef[i][j]=lef[i][j-1];
for(int i=1;i<=n;i++)
for(int j=m-1;j>=1;j--)
if(point[i][j]!=point[i][j+1])
righ[i][j]=righ[i][j+1];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(i>1&&point[i][j]!=point[i-1][j])
{
lef[i][j]=max(lef[i][j],lef[i-1][j]);
righ[i][j]=min(righ[i][j],righ[i-1][j]);
height[i][j]=height[i-1][j]+1;
}
int x=righ[i][j]-lef[i][j]+1;
int y=min(x,height[i][j]);
maxx1=max(maxx1,y*y);
maxx2=max(maxx2,x*height[i][j]);
}
cout<<maxx1<<endl<<maxx2;
return 0;
}
P1165 日志分析
栈的基操~~
代码:
#include<bits/stdc++.h>
using namespace std;
stack<int> s;
stack<int> st;
int n;
int com;
int wei;
int cnt;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>com;
if(com==0)
{
cin>>wei;
s.push(wei);
if(st.empty()||st.top()<wei)
st.push(wei);
else
{
int k=st.top();
st.push(k);
}
}
if(com==1)
{
if(s.empty())
{
cout<<0<<endl;
continue;
}
s.pop();
st.pop();
}
if(com==2)
{
if(s.empty())
{
cout<<0<<endl;
continue;
}
cout<<st.top()<<endl;
}
}
}
P1019 [NOIP2000 提高组] 单词接龙
看明白了两种做法诶,思路差不多的:层层推进,判断原字符串与新字符串能否实现接龙,那么不必对两个字符串进行操作,只需标记使用一次,并把长度加上即可(我一开始执着于对原字符串操作一下带进下一次dfs。。)
第一种:
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=25;
string s[N];
int vis[N];
int n;
int ans;
int maxx;
int lf(string x,string y)
{
for(int i=1;i<min(x.length(),y.length());i++)
{
int flag=1;
for(int j=0;j<i;j++)
if(x[x.length()-i+j]!=y[j])
flag=0;
if(flag)
return i;
}
return 0;
}
void dfs(string str,int ans)
{
maxx=max(maxx,ans);
for(int i=0;i<n;i++)
{
if(vis[i]>=2)
continue;
int c=lf(str,s[i]);
if(c>0)
{
vis[i]++;
dfs(s[i],ans+s[i].length()-c);
vis[i]--;
}
}
}
int main()
{
cin>>n;
memset(vis,0,sizeof(vis));
for(int i=0;i<=n;i++)
cin>>s[i];
dfs(' '+s[n],1);
cout<<maxx;
}
第二种:
基本与字符串分开,全程用数字操作
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=25;
string s[N];
int vis[N];
int n;
int maxx;
char a;
int ss;
int lf(int x,int y)
{
int kk;
for(int i=1;i<min(s[x].length(),s[y].length());i++)
{
int flag=1;
for(int j=0;j<i;j++)
if(s[x][s[x].length()-i+j]!=s[y][j])
flag=0;
if(flag)
return s[y].length()-i;
}
return 0;
}
void dfs(int p)
{
for(int i=1;i<=n;i++)
{
if(vis[i]<2&&lf(p,i))
{
int c=lf(p,i);
ss+=c;
vis[i]++;
dfs(i);
ss-=c;
vis[i]--;
}
}
maxx=max(maxx,ss);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>s[i];
cin>>a;
for(int i=1;i<=n;i++)
{
if(s[i][0]==a)
{
ss=s[i].length();
vis[i]++;
dfs(i);
vis[i]--;
maxx=max(maxx,ss);
}
}
cout<<maxx;
}
第一天ooo,完结撒花!!!