笔记

判断素数:

#include<iostream>
#include<math.h>
using namespace std;
bool su(int n){
	int i=2;
	if(n==1)
		return false;
	for(i=2;i<=sqrt(n);i++){
		if(n%i==0)
			break;
	}
	if(i>sqrt(n))
		return true;
else
	return false;
}

埃氏筛:

bool isnp[MAXN]; // is not prime: 不是素数
void init(int n)
{
    for (int i = 2; i * i <= n; i++)
        if (!isnp[i])
            for (int j = i * i; j <= n; j += i)
                isnp[j] = 1;
}

二分模板:

模板1:

while (l < r)
    {
        int mid = l + r >> 1;	//(l+r)/2
        if (check(mid))  r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }

模板2:

while (l < r)
    {
        int mid = l + r + 1 >> 1;	//(l+r+1)/2
        if (check(mid))  l = mid;
        else r = mid - 1;
    }

模板3:(浮点二分)

while(r-l>1e-5) //需要一个精度保证
{
	double mid = (l+r)/2;
	if(check(mid)) l=mid; //或r=mid;
	else r=mid; //或l=mid;
}

并查集:

一般模板:

int Parent[MAXN];
//初始化,现在各自为王,自己就是一个集合
void init(int n){
    for(int i=0;i<n;i++){
        Parent[i]=i;
    }
}
//查询根结点
int find(int x){
    if(Parent[x]==x)
        return x;
    else{
            Parent[x]=find(Parent[x]);   //顺便把双亲结点也设置为根结点,路径压缩
            return Parent[x];
    }
}
//合并,把 j 合并到 i 中去,就是把j的双亲结点设为i
void merge(int i,int j){
    Parent[find(j)] = find(i);
}

并查集按秩合并

//初始化
void init(int n){
    for(int i=0;i<n;i++){
        Parent[i]=i;
        Rank[i]=1;
    }
}
//合并
//合并
void merge(int i,int j){
    int x = find(i),y = find(j); //分别找到结点i和结点j的根节点
    if(Rank[x] < Rank[y]){       //如果以x作为根结点的子树深度小于以y作为根结点的子树的深度,则把x合并到y中
        Parent[x]=y;
    }
    else{
        Parent[y]=x;
    }
    if(Rank[x] == Rank[y] && x != y){  //如果深度相同且根结点不同,以x为根结点的子树深度+1
        Rank[x]++;
    }
}
//哈希表,搜索,优先队列

优先队列

struct node {
	int x,y;
	bool operator < (const node &b) const{
		return this->x>b.x;//按x从小到大排列
      //return this->x<b.x;//按x从大到小排列
	}
};
priority_queue<node>que;
priority_queue<int,vector<int>,greater<int> > que1;//从小到大排列
priority_queue<int> que2;//默认从大到小排列

初始化

按照单元赋值,将一个区间的元素都赋同一个值
在头文件<algorithm>里面
例如int一维数组arr[MAX]:fill(arr, arr + n, 要填入的内容);
int二维数组arr[N][M]:fill(arr[0],arr[0]+N*M,要填入的内容);
vector<int>v1; fill(v1.begin(),v1.end(),2); fill(v1.begin(),v1.begin()+4,4);
memset(arr,0,sizeof(arr));//只能赋0或-1;

vector:

vector<int> v1;
vector<father> v2;
vector<string> v3;
vector<vector<int> >;  //注意空格。这里相当于二维数组int a[n][n];
vector<int> v5 = { 1,2,3,4,5 }; //列表初始化,注意使用的是花括号
vector<string> v6 = { "hi","my","name","is","lee" };
vector<int> v7(5, -1); //初始化为-1,-1,-1,-1,-1。第一个参数是数目,第二个参数是要初始化的值
vector<string> v8(3, "hi");
vector<int> v9(10); //默认初始化为0
vector<int> v10(4); //默认初始化为空字符串

访问vector中的元素:

for (int i = 0; i < v1.size(); i++)
{
	cout << v1[i] << endl;
	v1[i] = 100;//只能修改已存在的元素
	cout << v1[i] << endl;
}
迭代器访问元素
vector<string> v6 = { "hi","my","name","is","lee" };
for (vector<string>::iterator iter = v6.begin(); iter != v6.end(); iter++)
{
	cout << *iter << endl;
	//下面两种方法都都可以检查迭代器是否为空
	cout << (*iter).empty() << endl;
	cout << iter->empty() << endl; 
}
/*反向迭代器*/
for (vector<string>::reverse_iterator iter = v6.rbegin(); iter != v6.rend(); iter++)
{	cout << *iter << endl; }

最短路径

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXV =1000; //最大顶点数
const int INF = 100000000; //设INF为一个很大的数
int n, m, s, G[MAXV][MAXV]; //n为顶点数,m为边数,s为起点
int d[MAXV]; //起点到达各点的最短路径长度
bool vis[MAXV] = {false}; //标记数组,vis[i]==true表示已访问。初值均为false
void Dijkstra(int s) //s为起点
{
	fill(d, d + MAXV, INF); //fill函数将整个d数组赋为INF
	d[s] = 0; //起点s到达自身的距离为0
	for(int i = 0; i < n; i++) //循环n次
	{
		int u = -1, MIN = INF; //u使得d[u]最小,MIN存放该最小的d[u]
		for(int j = 0; j < n; j++) //找到未访问的顶点中d[]最小的
		{
			if(vis[j] == false && d[j] < MIN)
			{
				u = j;
				MIN = d[j];
			}
		} 
		//找不到小于INF的d[u],说明剩下的顶点和起点s不连通
		if(u == -1)
			return;
		vis[u] = true; //标记u为已访问
		for(int v = 0; v < n; v++)
		{
		    //如果v未访问 && u能到达v && 以u为中介点可以使d[v]更优
			if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v])
			{
				d[v] = d[u] + G[u][v]; //优化d[v]
			}
		}
	}
}
int main()
{
	int u, v, w;
	scanf("%d%d%d", &n, &m, &s); //顶点个数、边数、起点编号
	fill(G[0], G[0] + MAXV * MAXV, INF); //初始化图G
	for(int i = 0; i < m; i++)
	{
		scanf("%d%d%d", &u, &v, &w); //输入u,v以及u->v的边权
		G[u][v] = w;
	}
	Dijkstra(s); //Dijkstra算法入口
	for(int i = 0; i < n; i++)
	{
		printf("%d ", d[i]); //输出所有顶点的最短距离
	}
	return 0;
}

背包问题

01背包(每个物品只能选一次)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int v[MAXN];    // 体积
int w[MAXN];    // 价值 
int f[MAXN][MAXN];  // f[i][j], j体积下前i个物品的最大价值 
int main() 
{
    int n, m;   
    cin >> n >> m;
    for(int i = 1; i <= n; i++) 
        cin >> v[i] >> w[i];

    for(int i = 1; i <= n; i++) 
        for(int j = 1; j <= m; j++)
        {
            //  当前背包容量装不进第i个物品,则价值等于前i-1个物品
            if(j < v[i]) 
                f[i][j] = f[i - 1][j];
            // 能装,需进行决策是否选择第i个物品
            else    
                f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
        }    
    /* 优化for(int i = 1; i <= n; i++)
{
    for(int j = m; j >= v[i]; j--)  
        f[j] = max(f[j], f[j - v[i]] + w[i]);
} */

    cout << f[n][m] << endl;

    return 0;
}
/*优化
#include<bits/stdc++.h>

using namespace std;

const int MAXN = 1005;
int f[MAXN];  // 

int main() 
{
    int n, m;   
    cin >> n >> m;

    for(int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;      // 边输入边处理
        for(int j = m; j >= v; j--)
            f[j] = max(f[j], f[j - v] + w);
    }

    cout << f[m] << endl;

    return 0;
}
*/

完全背包(每个物品可以选无限次)

#include<iostream>
using namespace std;
const int N = 1010;
int f[N];
int v[N],w[N];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i = 1 ; i <= n ;i ++)
    {
        cin>>v[i]>>w[i];
    }
/*
优化前
for(int i = 1 ; i <= n ; i++)
for(int j = 0 ; j <= m ; j ++)
{
    f[i][j] = f[i-1][j];
    if(j-v[i]>=0)
        f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
*/
    for(int i = 1 ; i<=n ;i++)
    for(int j = v[i] ; j<=m ;j++)
    {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    }
    cout<<f[m]<<endl;
}

多重背包

#include <bits/stdc++.h>
using namespace std;
int a[10005],b[10005],t=0,n,m,dp[10005]={ },w,v,s;
int main()
{
    cin>>n>>m;//物品种数和背包容积
    while(n--)
    {
        cin>>v>>w>>s;//第i种物品的体积和价值
        while(s--)
        {
            a[++t]=v;
            b[t]=w;
        }//死拆,把多重背包拆成01背包
    }
    for(int i=1;i<=t;i++)
    for(int j=m;j>=a[i];j--)
    dp[j]=max(dp[j-a[i]]+b[i],dp[j]);//直接套01背包的板子
    cout<<dp[m]<<endl;
    return 0;
}

前缀和

在一个n*m的只包含 0 和 1 的矩阵里找出一个不包含0的最大正方形,输出边长。

输入文件第一行为两个整数n,m,接下来n 行,每行 m个数字,用空格隔开,$0$ 或 $1$。

一个整数,最大正方形的边长。

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	int n,m,a[101][101]={0};
	cin>>n>>m;
	for(int i=1;i<=n;i++){
	for(int j=1;j<=m;j++){
		cin>>a[i][j];}}
	for(int i=1;i<=n;i++){
	for(int j=1;j<=m;j++){
	a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];}}
	int ans=1,l=2;
	while(l<=min(n,m)){
		for(int i=l;i<=n;i++){
		for(int j=l;j<=m;j++){
		if(a[i][j]-a[i-l][j]-a[i][j-l]+a[i-l][j-l]==l*l)
			ans=max(ans,l);}}
		l++;
	}
	cout<<ans;
	return 0;
}

差分

//要令某一段数组增加,假设要令从m到n增加x,则先求出差分数组c[max],令c[m]+=x,令c[n+1]-=x,之后再求前缀和
posted @ 2023-05-25 16:00  wanyki  阅读(48)  评论(0)    收藏  举报