DAY_1(STL)

寒假集训第一天!!欧耶!!>_<!!

好久前就听说过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,完结撒花!!!

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值