蓝桥杯,,

目录

BFS

八数码(模板)

卡片换位(同上)

青蛙跳杯子(同上)

链表

整数删除(双向链表+堆)

消除游戏

打印大X

打印十字图(模拟,看图)

核桃的数量 (最大公因数,最小公倍)

航班时间

三国游戏 (贪心)

耐摔指数 dp

缩位求和

交换瓶子

移动距离

等差数列

错误票据

刷题统计

日志统计

后缀表达式

连号区间数

统计 子矩阵

修剪灌木

分巧克力

乘法表

蚂蚁感冒

兰顿蚂蚁(模拟)

翻硬币

子串简写

走迷宫bfs(模板)  队列

全球变暖  bfs

穿越雷区

dfs(模板)

九宫幻方

排列数字  (堆)

小朋友的崇拜圈(真题)

飞机降落(真题)

   正则表达式(真题)********

 大臣的旅费(真题)+树的直径

树的直径 C++题解:树的直径_c++树的直径-CSDN博客

spfa

作物杂交

 dp

砝码称重

松散子序列

垒筛子

地宫取宝

画中漂流

数字三角形

最优包含

走方格dfs

区间DP

石子合并(模板)

          合并石子(真题)比上面多了颜色属性

合并果子(贪心)(haffman树)

数组切分

糖果

李白打酒


BFS

八数码(模板)

#include<bits/stdc++.h>

using namespace std;

int bfs(string state)
{
    queue<string> q; //定义队列
    unordered_map<string,int> d; //通过哈希表来让字符串变化时和移动的距离数值关联

    q.push(state); //将字符串入队
    d[state]=0; //将初始状态的字符串的哈希值设定为1

    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1}; //定义四个方向向量

    string end="12345678x"; //定义宽度优先搜素的终止状态
    while(q.size()) //循环终止状态
    {
        auto t=q.front(); //将队列中存着的字符串赋值给t
        q.pop(); //队头元素弹出

        if( t==end ) return d[t]; //如果当前字符串等于终止状态搜索结束返回该字符串对应的哈希值
                                  //此处的哈希函数值对应于字符串移动的次数    
        int distance = d[t]; //定义一个临时变量distance存储形成t字符串当前的移动次数
        int k = t.find('x'); //k表示'x'字符在字符串当前的下标
        int x = k/3,y = k%3; //由于字符串当前是一维的将一维下标转化为二维坐标
        for(int i=0;i<4;i++) //分别遍历四个方向
        { 
            int a=x+dx[i],b=y+dy[i]; //将下一个搜索位置的x,y坐标表示
            if(a>=0&&a<3&&b>=0&&b<3) //当二维坐标满足位于3X3矩阵中时
            {
                swap(t[a*3+b],t[k]); //将字符串中的搜索位置与字符'x'交换
                if(!d.count(t)) //如果当前的字符串的哈希值为0
                {
                    d[t]=distance+1; //将该字符串对应的哈希值在原字符串对应的哈希值基础上加1
                    q.push(t); //将该字符串入队
                }
                swap(t[a*3+b],t[k]); //恢复现场,返回位置判断其他方向
            }
        }
    }

    return -1; //如果无法移动到终止位置返回-1
}

int main()
{
    char s;

    string state;
    for(int i=0;i<9;i++)
    {
        cin>>s;
        state+=s; //逐个输入字符串
    }

    cout<<bfs(state)<<endl; //输出宽度优先搜索的数值

    return 0;
}

卡片换位(同上)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>

using namespace std;

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int a, b; //记录起始A、B下标

int bfs(string start)
{
    //定义bfs队列和dist数组
    queue<string> q;
    unordered_map<string, int> d;

    //初始化队列和dist数组
    q.push(start);
    d[start] = 0;

    while (q.size())
    {
        //取队头元素
        auto t = q.front();
        q.pop();

        //记录当前状态的距离,如果是最终状态则返回距离
        int distance = d[t];
        if (t.find('A') == b && t.find('B') == a) return distance; 

        //查询 ' '在字符串中的下标,然后转换为在矩阵中的坐标
        int k = t.find(' ');

        //将一维坐标转换为二维 
        int x = k / 3, y = k % 3;

        for (int i = 0; i < 4; i ++ )
        {
            //转移后 ' '的坐标
            int xx = x + dx[i], yy = y + dy[i];

            //若当前坐标没有越界
            if (xx >= 0 && xx < 2 && yy >= 0 && yy < 3)
            {
                //转移状态
                swap(t[k], t[xx * 3 + yy]);

                //如果当前状态没被遍历过 记录距离 入队
                if (!d.count(t))
                {
                    d[t] = distance + 1;
                    q.push(t);
                }

                //还原状态,为下一种情况做准备
                swap(t[k], t[xx * 3 + yy]);
            }
        }
    }
}
int main()
{
    
    
    
 string start;
    for (int i = 0; i < 2; i ++ )
    {
        string tmp;
        getline(cin, tmp);//*********** 读取带空格的字符串,整行读取  *********八数码中空格不算字符  而此题空格算字符 
        start += tmp;
    }
    for (int i = 0; i < 6; i ++ )
    {
        if (start[i] == 'A') a = i;
        if (start[i] == 'B') b = i;
    }

    cout << bfs(start) << endl;
    return 0;
}

青蛙跳杯子(同上)

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
queue<string> q;
unordered_map<string,int> d;
int dx[6]={-2,-1,1,2,-3,3};
int n;
//string start,endss;
char start[20];
char endss[20];
int bfs()
{
    q.push(start);
    d[start]=0;
    
    while(q.size())
    {
        auto t=q.front();
        
        q.pop();
        int distance=d[t];//1
        if(t==endss) return distance;
        
        int pos=t.find('*');
            
        for(int i=0;i<6;i++)
        {
            int nx=pos+dx[i];
            if(nx>=0&&nx<n)
            {
                swap(t[nx],t[pos]);
                if(d.count(t)==0)  //
                {
                    q.push(t);
                   d[t]=distance+1;//1
                }
                
                swap(t[pos],t[nx]);
            }
            
        }
    }
    return -1;
}
int main()
{/**********************
   scanf("%s") 不能用于读string a
*/
    
    
  // cin>>start>>endss;
   scanf("%s",start);
   scanf("%s",endss);
    /*getline(cin,start);
    getline(cin,endss);*/
    //scanf %s 不行
    n = strlen(start);//

    cout << bfs() << endl;

    return 0;
}

链表

整数删除(双向链表+堆)

AcWing 4961. 整数删除 | 堆 | 双向链表 - AcWing   题解

//双向链表   堆
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 5e5 + 10;
ll v[N], l[N], r[N];//v:权值


void del(int x) 
{
    r[l[x]] = r[x], l[r[x]] = l[x];
    v[l[x]] += v[x], v[r[x]] += v[x];
}

int main () 
{
    int n, k; cin >> n >> k;
    r[0] = 1, l[n + 1] = n; //0和n+1 情况   r[0]   此时1是头,后面可能指针变r[0]不一定等于1 但r[0]是头
    //l[1]=0,r[n]=n+1; 错
    priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> h;//堆 找min
    for (int i = 1; i <= n; i ++)
        cin >> v[i], l[i] = i - 1, r[i] = i + 1, h.push({v[i], i});
    while (k --) 
    {
        auto p = h.top(); h.pop();
        if (p.first != v[p.second]) h.push({v[p.second], p.second}), k ++;//没删,不应该占操作数k, while k--了,要++ 
        else del(p.second);
    }
    int head = r[0];//r[0]***头不一定是1了,双向链表,位置不变,但指针已经变了,已经删了****
    while (head != n + 1) //合法
    {
        cout << v[head]<< " ";
        head = r[head];//链表往后走 
    }
    return 0;
}

消除游戏

//快速删除:双链表
//边缘字母只可能在左右两边发生变化
//q:备选集合,w:待删集合
//以后每次从备选删的里找。每次删都会改变被删,所以每次清空被删
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=1000010;
char s[N];//并未真正删除s中的元素,只是逻辑修改了下标
int l[N],r[N];
int st[N];//标记下表是否被删除
vector<int> q,w;//q表示备删的元素,w表示待删元素
void insert(int temp)//insert函数
{
    if(st[temp]==0)//不能已经删过了   有可能两个元素在不同情况里都被删(判重)   如:00xx
    {
        st[temp]=1;
        w.push_back(temp);
    }
}
void filterfun()
{
    w.clear();//清空要删除的元素
   for(int t:q)//遍历备删元素,找到符合条件的待删元素
    {
        int a=l[t],b=t,c=r[t];
        if(s[a]==s[b]&&s[b]!=s[c]&&s[c]!='#') //如果是#aa#,那么b和c就不能插入到待删元素,多举例子就懂了
        {                                     
            insert(b);
            insert(c);
        }
        if(s[a]!=s[b]&&s[b]==s[c]&&s[a]!='#')
        {
            insert(a);
            insert(b);
        }
    }
}
int main()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    s[0]='#',s[n+1]='#';//为数组设置边界
    for(int i=1;i<=n;i++)//双链表初始化,注意不是循环双链表,与基础课中的区分
    {
        l[i]=i-1;
        r[i]=i+1;
        q.push_back(i);//初始化备删元素
    }
    r[0]=1;
    l[n+1]=n;
    while(1)//这里数组的长度远小于2的64次方,所以一定在数组长度的时间范围内退出循环,不会TLE
    {
        filterfun();//找到待删的下标         
        if(w.empty()) break;//表示没有待删的元素退出循环
        q.clear();//每退出一次查找,要清空备选
        for(int t:w)//删元素   并找备删元素
        {
            int a=l[t],b=t,c=r[t];
            if(a!=0&&st[a]==0&&(q.empty()||a!=q.back())) q.push_back(a);//备删元素不可以是下标0,且该元素不可以被删,且不可以重复插入
            if(c!=n+1&&st[c]==0)q.push_back(c);
            r[a]=c;
            l[c]=a;
        }
    }
    if(r[0]==n+1) puts("EMPTY");//已经没有字符
    else{
        for(int i=r[0];i!=n+1;i=r[i])
         cout<<s[i];
    }
    return 0;
}

打印大X

#include<iostream>
using namespace std;
char g[2010][2010];//len=m+n-1=2010
int main()
{
    int m,n;
    cin>>m>>n;
    
    int len=n+m-1;//长度*******************
    for(int i=0;i<(n+1)/2;i++)//上半边
    {
        for(int j=0;j<len;j++) g[i][j]='.';//先一行赋初值.,下面改*
        
        int s1=i,s2=(len-1-i);
        for(int j=0;j<m;j++)//左右均4个*
        {
            //if(s1>s2) break;
            g[i][s1++]=g[i][s2--]='*';//一行左右同时*
        }
    }
    for(int i=0;i<(n+1)/2;i++)//打印上半边
    {
        for(int j=0;j<len;j++) cout<<g[i][j];
        
        cout<<endl;
    }
    
    for(int i=(n+1)/2-2;i>=0;i--)//对称 少一行
    {
        for(int j=0;j<len;j++) cout<<g[i][j];
        
        cout<<endl;
    }
    
    return 0;
}

打印十字图(模拟,看图)

#include<stdio.h>
#define N 1000
int main(){
    int a,b,c=0;
    char aa[N][N];
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<N;j++)
        {
            aa[i][j]='0';//数组全部初始化为0,也可以用memset
        }
    }
    scanf("%d",&a);//接受需要打印的层数
    b=1+a*4+4;
    for(int i=b/2-2;i<=b/2+2;i++)aa[i][b/2]='$';
    for(int i=b/2-2;i<=b/2+2;i++)aa[b/2][i]='$';//初始化最内层的十字架


    while(c<a)
    {
        for(int i=0;i<b;i++)
        {
            for(int j=0;j<b;j++)
            {
                if(aa[i][j]=='0'&&((aa[i-1][j]=='$'||aa[i][j-1]=='$'||aa[i+1][j]=='$'||aa[i][j+1]=='$'||aa[i-1][j-1]=='$'||aa[i+1][j+1]=='$'||aa[i-1][j+1]=='$'||aa[i+1][j-1]=='$')))
                {
                    aa[i][j]='.';//采用洪泛反复循环,由内向外$和.交替生成
                }

            }

        }
        for(int i=0;i<b;i++)
        {
            for(int j=0;j<b;j++)
            {
                if(aa[i][j]=='0'&&((aa[i-1][j]=='.'||aa[i][j-1]=='.'||aa[i+1][j]=='.'||aa[i][j+1]=='.'||aa[i-1][j-1]=='.'||aa[i+1][j+1]=='.'||aa[i+1][j-1]=='.'||aa[i-1][j+1]=='.')))
                {
                    aa[i][j]='$';//采用洪泛反复循环,由内向外$和.交替生成
                }

            }

        }
        c++;
    }
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            aa[i][j]='.';
        }
        for(int j=b-1;j>b-3;j--)
        {
            aa[i][j]='.';//把四个角上的符号变成点
        }
    }
    for(int i=b-1;i>b-3;i--)
    {
        for(int j=0;j<2;j++)
        {
            aa[i][j]='.';
        }
        for(int j=b-1;j>b-3;j--)
        {
            aa[i][j]='.';//把四个角上的符号变成点
        }
    }

    for(int i=0;i<b;i++)
    {
        for(int j=0;j<b;j++)
        {
            printf("%c",aa[i][j]);//输出十字图
        }
        printf("\n");
    }
}

核桃的数量 (最大公因数,最小公倍)

#include<iostream>
using namespace std;
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
int main()
{
    int a,b,c;
    cin>>a>>b>>c;
    int num1=a*b/gcd(a,b);
    int num2=num1*c/gcd(num1,c);
    
    cout<<num2;
    
}

或遍历

航班时间


//去乘起飞时间+航行时间+时差=去乘降落时间 
//回程起飞时间+航行时间-时差=回程降落时间  
//求:航行时间

//航行时间=(去乘降落时间-去乘起飞时间+回程降落时间-回程起飞时间)/2

#include<bits/stdc++.h>
using namespace std;
int getTime(void)
{
    int h1,m1,s1,h2,m2,s2,d=0;
    scanf("%d:%d:%d %d:%d:%d (+%d)",&h1,&m1,&s1,&h2,&m2,&s2,&d);
    int time=d*24*3600+h2*3600+m2*60+s2-(h1*3600+m1*60+s1);
    return time;
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int i = 0; i < t; i++)
    {
        int time1=getTime();
        int time2=getTime();
        int t=(time1+time2)/2;
        printf("%02d:%02d:%02d\n", t/3600, t/60%60, t%60);
    }
    return 0;
}

三国游戏 (贪心)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;

typedef long long LL;

int n;
int A[N], B[N], C[N], W[N];


int check(int x[], int y[], int z[]) {
    for (int i = 1; i <= n; i++) {
        W[i] = x[i] - y[i] - z[i];
    }

    sort(W + 1, W + n + 1, greater<int>());

    int res = -1;
    LL sum = 0;

    for (int i = 1; i <= n; i++) {
        sum += W[i]; //前缀和
        if (sum > 0) res = i;
        else break;
    }
    return res;
}

int main() {
    cin >> n;

    for (int i = 1; i <= n; i++) cin >> A[i];
    for (int i = 1; i <= n; i++) cin >> B[i];
    for (int i = 1; i <= n; i++) cin >> C[i];

    int ans = max({check(A, B, C), check(B, A, C), check(C, A, B)});

    cout << ans;

    return 0;
}

耐摔指数 dp

//从每个厂家抽样3部手机参加测试
#include<iostream>
using namespace std;  //i手机,j层楼
int dp[5][10010];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=3;i++)
    {
        for(int j=0;j<=n;j++) dp[i][j]=j;
    }
    for(int i=2;i<=3;i++) //手机
    {
        for(int j=1;j<=n;j++) //楼
        {
            for(int k=1;k<=j;k++) //中间情况
            {
                dp[i][j]=min(dp[i][j],max(dp[i-1][k-1],dp[i][j-(k+1)+1]) +1 ); //+1 :+k的那个  k-1
                //max(dp[i-1][k-1],dp[i][j-(k+1)+1])  +1      最坏运气
                
            }
        }
        
       
    }
    cout<<dp[3][n];
    
}

缩位求和

248×15=3720248×15=3720

把乘数和被乘数分别逐位求和,如果是多位数再逐位求和,直到是 11 位数,得

输入:

一个由数字组成的串,表示 n� 位数。

输出

一位数,表示反复逐位求和的结果。

2 + 4 + 8 = 14 ==> 1 + 4 = 5;
1 + 5 = 6
5 * 6
#include<iostream>
#include<cstring>
using namespace std;
char a[1010];
int b[1010];
int main()
{
     long long ans=0,num=0;
    scanf("%s",a);
    for(int i=0;i<strlen(a);i++)
   
   {
       b[i]=a[i]-'0';
       num+=b[i];  //第一层
   }
   
   while(num>=10)
   {
       ans=0;
       while(num)
       {
           ans+=num%10;//新的和
           num/=10;
       }
       num=ans;  //判断新的和是否>=10
   }
    cout<<ans;//最后num=ans
}

交换瓶子

#include<iostream>
using namespace std;
int a[100010];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]!=i)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(a[j]==i)
                {
                    int t=a[i];
                    a[i]=a[j];
                    a[j]=t;
                    
                    
                    ans++;
                    break;
                }
            }
        }
    }
    cout<<ans<<endl;
}

移动距离

X星球居民小区的楼房全是一样的,并且按矩阵样式排列。

其楼房的编号为 1,2,3…1,2,3…

当排满一行时,从下一行相邻的楼往反方向排号。

比如:当小区排号宽度为 66 时,开始情形如下:

1  2  3  4  5  6
12 11 10 9  8  7
13 14 15 .....

我们的问题是:已知了两个楼号 m� 和 n�,需要求出它们之间的最短移动距离(不能斜线方向移动)。

输入共一行,包含三个整数 w,m,n�,�,�,w� 为排号宽度,m,n�,� 为待计算的楼号。

输出一个整数,表示 m,n�,� 两楼间最短移动距离。

1≤w,m,n≤100001≤�,�,�≤10000,

6 8 2
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    
    int w,m,n;
    cin>>w>>m>>n;
    int hang1=(m-1)/w+1;
    int hang2=(n-1)/w+1;
    int ans;
    int  lie1,lie2;
    if(hang1%2==0)
    {
        lie1=w-m%w+1;
        
    }
    else
    {
        if(m%w) lie1=m%w;
        else lie1=w;
    }
    
    if(hang2%2==0)
    {
        lie2=w-n%w +1;
    }
    else
    {
        if(n%w) lie2=n%w;
        else lie2=w;
    }
    
   ans=abs(hang1-hang2)+abs(lie1-lie2);
    cout<<ans;
}

等差数列

数学老师给小明出了一道等差数列求和的题目。

但是粗心的小明忘记了一部分的数列,只记得其中 N� 个整数。

现在给出这 N� 个整数,小明想知道包含这 N� 个整数的最短的等差数列有几项?

输入的第一行包含一个整数 N�。

第二行包含 N� 个整数 A1,A2,⋅⋅⋅,AN�1,�2,···,��。(注意 A1∼AN�1∼�� 并不一定是按等差数
列中的顺序给出)

输出一个整数表示答案。

2≤N≤1000002≤�≤100000,
0≤Ai≤1090≤��≤109

5
2 6 4 10 20
10

包含 2、6、4、10、202、6、4、10、20 的最短的等差数列是 2、4、6、8、10、12、14、16、18、202、4、6、8、10、12、14、16、18、20。

//各差值最大公因数 是本题最大公差
#include<iostream>

#include<algorithm>
using namespace std;
int a[100010];
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;//********
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    sort(a,a+n);
    int gd=0;  //***如:1 1 1        0与x的最大公因数:x        1与x最大公因数:1
    for(int i=0;i<n-1;i++)
    {
        gd=gcd(gd,a[i+1]-a[i]);//若a[i]-a[0]  范围太大效果不好
        
    }
    if(gd==0) cout<<n<<endl;
    else
    {
        int ans=(a[n-1]-a[0])/gd+1 ;
        cout<<ans<<endl;
    }
}

错误票据

刷题统计

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main() {
    LL a, b, n, ret;
    cin >> a >> b >> n;
    ret = n / (a * 5 + b * 2) * 7;
    n -= (a * 5 + b * 2) * ret / 7;

    for (int i = 0; i < 5 && n > 0; i ++) ret ++, n -= a;
    for (int i = 0; i < 2 && n > 0; i ++) ret ++, n -= b;

    cout << ret << endl;
    return 0;
}

日志统计

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100010;   //根据题目数据范围,定N大小,此题则主要根据N,ts,id来确定的
typedef pair<int, int> PII; //定义一个PII类型以及一对int整型用于存储ts和id

#define x first //为了代码简洁,则分别把first, second 宏定义为x, y
#define y second
int n, d, k;
int cnt[N]; //用于存储每个id号获得赞数,所以后面代码为cnt[t] ++;
bool st[N]; //用于存储每个帖子是否是热帖,所以用bool类型
PII logs[N];//用于存储日志ts, id

int main()
{
    scanf("%d %d %d", &n, &d, &k);
    for(int i = 0; i < n; i ++) scanf("%d %d\n", &logs[i].first, &logs[i].second);

    sort(logs, logs + n);//以第一个参数排序 

    for(int i = 0, j = 0; i < n; i ++)//双指针算法,i在前,j在后
    {
        int t = logs[i].second;//把每个获赞的帖子id存入t
        cnt[t] ++;//获得一个赞,所以此刻 ++;
        while(logs[i].first - logs[j].first >= d)//如果俩个帖子时间相差超过d,说明该赞无效
        {
            cnt[logs[j].second] --;//所以此刻--;
            j ++;//要把指针j往后,否则死循环
        }
        if(cnt[t] >= k) st[t] = true; //如果该id贴赞超过k,说明是热帖
    }
    for(int i = 0; i < 100000; i ++)
    {
        if(st[i])//如果为真,则把热帖的id打印出来
            cout << i << endl;
    }

    return 0;
}

后缀表达式

详细解答:AcWing 1247. 后缀表达式 最详细题解!!! - AcWing

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=2e5+10;
int a[N];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n+m+1;i++) scanf("%d",&a[i]);
    LL res=0;
    if(m==0)  //特判,如果没有-号,就把序列全部加起来
    {
        for(int i=0;i<n+m+1;i++) res+=a[i];
    }
    else
    {
        sort(a,a+n+m+1);  //排序
        res-=a[0];  //减去最小的数
        res+=a[n+m];  //加上最大的数
        for(int i=1;i<n+m;i++)  res+=abs(a[i]);  //加上序列中间的数的绝对值
    }
    printf("%lld",res);
    return 0;
}

连号区间数

连号区间数(蓝桥杯)-CSDN博客

注意题目给出的就是1~n的某个排列

#include<iostream>
using namespace std;
int a[100010];
int main()
{
    int n;
    scanf("%d",&n);
  
    int min,max;
    for(int i=1;i<=n;i++) cin>>a[i];
   int ans=0;
    for(int i=1;i<=n;i++)
    {
         min=100000,max=0;
        for(int j=i;j<=n;j++)
        {
            if(a[j]>max) max=a[j];
            if(a[j]<min) min=a[j];
            
            if((max-min)==(j-i)) ans++;
        }
    }
    cout<<ans<<endl;
}

统计 子矩阵

4405. 统计子矩阵 - AcWing题库

二维 变一维

#include<iostream>
using namespace std;

typedef long long ll;
const int N = 5e2+3;
int n, m, k;
int a[N][N];


int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m >> k;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            cin >> a[i][j];
            a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
        }
    }

    ll ans = 0;
    for(int i=1; i<=m; i++){
        for(int j=i; j<=m; j++){
            for(int s = 1, t = 1; t <= n; t ++ ){
                while(s <= t && a[t][j] - a[s - 1][j] - a[t][i - 1] + a[s - 1][i - 1] > k) s ++ ;
                if(s <= t) ans += t - s + 1;
            }
        }
    }

    cout << ans << '\n';
}

修剪灌木

从左到右从右到左一个循环 每棵树都被剪两次
其中第i棵树两次被剪的间隔 要么是左边的i-1棵树 要么是右边的n-i棵树 被剪两次
故最高高度=2*max(m-i,i-1)

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)cout<<2*max(n-i,i-1)<<endl;
}

分巧克力

二分 check

儿童节那天有 K� 位小朋友到小明家做客。

小明拿出了珍藏的巧克力招待小朋友们。

小明一共有 N� 块巧克力,其中第 i� 块是 Hi×Wi��×�� 的方格组成的长方形。

为了公平起见,小明需要从这 N� 块巧克力中切出 K� 块巧克力分给小朋友们。

切出的巧克力需要满足:

  1. 形状是正方形,边长是整数
  2. 大小相同

例如一块 6×56×5 的巧克力可以切出 66 块 2×22×2 的巧克力或者 22 块 3×33×3 的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?

#include<iostream>
using namespace std;
int h[100010],w[100010];
int n,k;
bool check(int mid)
{
    int sum=0;
    for(int i=0;i<n;i++)
    {
        sum+=(h[i]/mid)*(w[i]/mid);
        
        if(sum>=k) return 1;//可放里面,已经满足条件提前结束
    }
    
   return 0;
}
int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++)
    {
        cin>>h[i]>>w[i];
    }
    int l=1,r=1e5;//边长不超过1e5
    int mid;
    while(l<r)  //等于时结束
    {
        mid=(l+r+1)/2;
        if(check(mid)) l=mid;  //找到最大的,mid可以,要看右边的行不行,
        else r=mid-1;  //mid不行  左边从Mid-1
        
     }
     cout<<l;  //r也行 mid 不行
}

乘法表

p 进制的乘法表就是多了一次进制转换。

写一个函数I2P,输出 十 进制的数,返回它对应的 p 进制下的结果。

字符串 好弄

#include<iostream>
using namespace std;


int p; //p进制
string jin(int a)
{
    string res;
    while(a)
    {
        int t=a%p;
        if(t<10) res=(char)(t+'0')+res;  //字符串 加数不能反过来  ‘a'+'b'=ab  
        //先出的余数在最右边,每出一个余数都放最左边
        else res=(char)('A'+t-10)+res;
        
        a/=p;
    }
    return res;
}
int main()
{
    cin>>p;
    for(int i=1;i<p;i++)
    {
        for(int j=1;j<=i;j++)
        {
            cout<<jin(i)<<"*"<<jin(j)<<"="<<jin(i*j)<<" ";
        }
        cout<<endl;
    }
}

蚂蚁感冒

先分两种大的情况,就是第一个感冒的蚂蚁向左还是向右
如果向右, 那么它右面向左走的一定会传染,右面向右走的一定不会传染
它左面向左走的一定不会传染,它左面向右走的会被它右面向左走的感染
如果向左,与上同理

交换

#include <iostream>  
#include <vector>  
#include <string> 
#include<cmath> 
#include<algorithm> 
using namespace std;  
int a[100010];
int cmp(int a,int b)
{
	return abs(a)<abs(b);
}
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	int temp=a[0];
	sort(a,a+n,cmp);
	int xia;
	for(int i=0;i<n;i++)
	{
		if(a[i]==temp)
		{
			xia=i;
		}
	}
	int num=1;//
	if(temp>0)
	{
		int  f=0;
		for(int i=xia+1;i<n;i++)
		{
			if(a[i]<0)
			{
				num++;
				f=1;
			}
		}
		if(f)
		{
			for(int i=0;i<xia;i++)
			{
				if(a[i]>0)
				{
					num++;
				}
			}
		}
	}
		else
	{
		int  f=0;
		for(int i=0;i<xia;i++)
		{
			if(a[i]>0)
			{
				num++;
				f=1;
			}
		}
		if(f)
		{
			for(int i=xia+1;i<n;i++)
			{
				if(a[i]<0)
				{
					num++;
				}
			}
		}
	}
	cout<<num<<endl;
 } 

兰顿蚂蚁(模拟)

注意思路!write by yourself

3152. 兰顿蚂蚁 - AcWing题库

#include<iostream>
using namespace std;
int Map[101][101];
int m, n;//正方形格子的行数和列数
int x, y,k;//x,y为蚂蚁所在的行号和列号,k为蚂蚁走的步数
char s;//蚂蚁头的朝向,上下左右UDLR
void Go() {
	if (Map[x][y] == 1) {//1为黑格,右转变白格
		Map[x][y] = 0;
		if (s == 'U') {
			s = 'R';
			y++;
		}
		else if (s == 'R') {
			s = 'D';
			x++;
		}
		else if (s == 'D') {
			s = 'L';
			y--;
		}
		else if (s == 'L') {
			s = 'U';
			x--;
		}
	}
	else {//0为白格,左转变黑格
		Map[x][y] = 1;
		if (s == 'U') {
			s = 'L';
			y--;
		}
		else if (s == 'R') {
			s = 'U';
			x--;
		}
		else if (s == 'D') {
			s = 'R';
			y++;
		}
		else if (s == 'L') {
			s = 'D';
			x++;
		}
	}
}
int main() {
	cin >> m >> n;
	for (int i = 0; i < m; i++) {//初始化地图
		for (int j = 0; j < n; j++) {
			cin >> Map[i][j];
		}
	}
	cin >> x >> y >> s >> k;
	while (k--) {
		Go();
	}
	cout << x << " " << y << endl;
}

翻硬币

//模拟法 从前往后翻硬币
#include<iostream>
using namespace std;
int main()
{
    int cnt=0;
    string a,b;
    cin>>a>>b;
    for(int i=0;i<a.size()-1;i++)
    {
        if(a[i]!=b[i])
        {
            cnt++;
            if(a[i]=='o') a[i]='*';
            else a[i]='o';
            
            if(a[i+1]=='o') a[i+1]='*';  //翻转第一组 与第二组对比
            else a[i+1]='o';
        }
    }
    cout<<cnt;
}

子串简写

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 5e5 + 10;

char str[N];
int s[N];
int n, k;
char st, ed;

int main()
{
    scanf("%d\n%s %c %c", &k, str + 1, &st, &ed);
    n = strlen(str + 1);
    LL res = 0;
    for (int i = 1, t = 0; i <= n; i++)
    {
        if (str[i] == st) t++;//
        s[i] = t;
        if (i >= k && str[i] == ed) res += s[i - k + 1];
    }
    printf("%lld\n", res);

    return 0;
}//前缀和

走迷宫bfs(模板)  队列


#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
const int N=110;
int n,m;
int g[N][N],d[N][N];

int bfs()
{ 
    queue<PII> q;//队列   
    
    d[0][0]=0;
    
 
    q.push({0,0});
   	int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
   
    while(q.size())
    {
        PII t=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int x=t.first+dx[i],y=t.second+dy[i];
            if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]==0&&d[x][y]==0  )//写在for循环里面
           {
            d[x][y]=d[t.first][t.second]+1;
           // q{x,y}=1;
            q.push({x,y});
           }
        }
        
        
    }
    cout<< d[n-1][m-1];
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++) cin>>g[i][j];
    }
    bfs();
    return 0;
    
}

全球变暖  bfs

//一个岛屿里有>=1个土地没接触到水就不算 完全浸湿
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define x first
#define y second
using namespace std;
char arr[1010][1010];
bool finddao[1010][1010];
int N;
bool bfs(pair<int,int>s1){
    int sum=0;//计算不被海水侵蚀的路地块个数
    bool mark;//用于判断十分被侵蚀
    int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};//初始化偏移量
    queue<pair<int,int>>dui;
    dui.push(s1);
    while(dui.size()){
        auto s2=dui.front();
        dui.pop();
        mark=1;
        for(int i=0;i<4;i++){
            int x1=s2.x+dx[i],y1=s2.y+dy[i];
            if(x1<0||x1>=N||y1<0||y1>=N)continue;
            if(arr[x1][y1]=='.'){mark=0;continue;}
            if(finddao[x1][y1])continue;
                finddao[x1][y1]=true;
                dui.push(make_pair(x1,y1));
        }
        if(mark==1)sum++;
    }
    if(sum>=1)//如果岛上有不被侵蚀的路地块那么这岛能存活
    return 1;
    else //这岛寄了
    return 0;
}
int main(){
    int allisland=0,lifeisland=0;
    pair<int,int> s1;
    cin>>N;
    for(int i=0;i<N;i++)cin>>arr[i];
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            if(arr[i][j]=='#'&&!finddao[i][j]){
                s1={i,j};
                allisland++;//计算所有岛的数目
                lifeisland+=bfs(s1);//计算能存活的岛
            }
        }
    }
    cout<<allisland-lifeisland;//用所有的岛数目减能留下的岛数目剩下就是沉下的岛
    return 0;
}

穿越雷区

/*bfs模板*/
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include <cstring>
using namespace std;
const int N=110;
bool st[N][N];
char g[N][N];
typedef pair<int,int> PII;
    int n;
    int f[N][N];
void dfs(int x,int y)
{
    f[x][y]=0;//***********
    int dx[4]={-1,1,0,0},dy[4]={0,0,1,-1};
    
    st[x][y]=true;
    queue<PII> q;
    q.push({x,y});
    
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int a=dx[i]+t.first,b=dy[i]+t.second;
            if(a<0||a>=n||b<0||b>=n||st[a][b]||g[a][b]==g[t.first][t.second]) continue;
            if(f[a][b]>f[t.first][t.second]+1)
                {f[a][b]=f[t.first][t.second]+1;
                q.push({a,b});//
                  st[a][b]=true;}//放不放{}里都行
        }
    }
}
int main()
{
    cin>>n;
    int ax,ay,bx,by;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            cin>>g[i][j];
            if(g[i][j]=='A') ax=i,ay=j;
            if(g[i][j]=='B') bx=i,by=j;
        }
    }
     memset(f,0x3f,sizeof f); //**************
    dfs(ax,ay);
    if(f[bx][by]==0x3f3f3f3f) cout<<"-1"<<endl;
    else cout<<f[bx][by]<<endl;
}

dfs(模板)

九宫幻方

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 15;
int g[N], ans[N];
bool st[N], used[N];     //位置, 数字 
int cnt;

void check()
{
    int t[8] = {0};
    t[0] = g[1] + g[2] + g[3];
    t[1] = g[4] + g[5] + g[6];
    t[2] = g[7] + g[8] + g[9];
    t[3] = g[1] + g[4] + g[7];
    t[4] = g[2] + g[5] + g[8];
    t[5] = g[3] + g[6] + g[9];
    t[6] = g[1] + g[5] + g[9];
    t[7] = g[3] + g[5] + g[7];

    for (int i = 0; i <= 7; i++) if (t[i] != 15) return;

    cnt++;

    for (int i = 1; i <= 9; i++) ans[i] = g[i];
}

void dfs(int x) // 第x个位置 
{
    if (cnt > 1) return;
    if (x > 9)
    {
        check();
        return;
    }

    if (st[x])//此位置已有数字
    {
        dfs(x + 1);
        return;
    }

    for (int i = 1; i <= 9; i++)
    {
        if (!used[i])
        {
            g[x] = i;
            used[i] = true;
            dfs(x + 1);
            used[i] = false;
        }
    }
}

int main()
{
    for (int i = 1; i <= 9; i++)//初始化
    {
        cin >> g[i];    
        if (g[i])
        {
            st[i] = true; //
            used[g[i]] = true; //此数字用过了
        }
    }

    dfs(1);
    if (cnt == 1) 
    {
        for (int i = 1; i <= 9; i++)
        {
            cout << ans[i] << ' ';
            if (i % 3 == 0) cout << endl;
        }
    }
    else cout << "Too Many";
    return 0;
}

排列数字  (堆)

输出1~n所用排列

123 132 213 231 312 321

#include<iostream>
using namespace std;
const int N=10;
int path[N];
bool st[10];
int n;
void dfs(int u)
{
    if(u==n)  ///到最后一个数字了
    {
        for(int i=0;i<n;i++ ) cout<<path[i]<<" "; //输出path数组(下标从0)
        cout<<endl;
        return;
    }
    
    for(int i=1;i<=n;i++)//找1~n里没被用过的数i
    {
        if(!st[i])
        {
            path[u]=i;   //当前位置 i
            st[i]=true;  //i用过了
            dfs(u+1);  //下一层
            st[i]=false; //回溯
           
        }
    }
  
}
int main()
{
    cin>>n;
    dfs(0);
    return 0;
}

小朋友的崇拜圈(真题)

a[i]:第i个人崇拜的人

AcWing 3170. 小朋友崇拜圈 - AcWing    题解

#include <iostream>
using namespace std;

const int N = 1e5 + 10;

int nextC[N]; //每个孩子最崇拜的孩子
int dfn[N]; //每个点的时间戳
int n, ans, idx;

int dfs(int u, int start)
{
    dfn[u] = ++idx;
    if (dfn[nextC[u]] && dfn[nextC[u]] >= start) //有可能时自环所以时>= 
        return dfn[u] - dfn[nextC[u]] + 1;
    if (dfn[nextC[u]] && dfn[nextC[u]] < start)   return 0;

    return dfs(nextC[u], start);
}

int main()
{
    cin>>n;
    for(int i = 1; i <= n; ++i) cin >> nextC[i];

    for(int i = 1; i <= n;++i)
    {
        if (!dfn[i])
            ans = max(ans, dfs(i, idx + 1));
    }
  cout<<ans;
  return 0;
}
​

飞机降落(真题)

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20;
bool st[N];
int n;
struct plane
{
	int t,d,l;
}p[N];
bool dfs(int u,int last)
{
	if(u==n)
	{
		return true;
	}
	for(int i=0;i<n;i++)
	{
		int t=p[i].t,d=p[i].d,l=p[i].l;
		if(!st[i]&&(t+d)>=last)
		{
			st[i]=true;
			if( dfs(u+1,max(last,t)+l)) return true;
		//不回溯(不是排列数字,数字要重复用)
		//这个飞机就排一次
		st[i]=false; //回溯,不满足的回去,不能弄别的回不来了 
		}
	}
	return false;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{  
	memset(st,0,sizeof st);
		cin>>n;
		for(int i=0;i<n;i++)
		{
			int t,d,l;
			cin>>t>>d>>l;
			p[i].t=t,p[i].d=d,p[i].l=l;
		
		}
		if(dfs(0,0)) puts("YES");
		else puts("NO");
	}
}

   正则表达式(真题)********

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std ;

int k ;
string str ;

int dfs(){
    int res = 0 ;
    while(k<str.size()){
        if(str[k] == '('){
            k ++ ;
            res += dfs() ;
            k ++ ;
        }else if(str[k] == ')'){
            // k ++ ;
            break ;
        }else if(str[k] == '|'){
            k ++ ;
            res = max(res,dfs()) ;
        }else{
            k ++ ;
            res ++ ;
        }
    }
    return res ;
}

int main(){
    cin >> str ;

    cout << dfs() << endl ;

    return 0 ;

}

 大臣的旅费(真题)+树的直径********

树的直径 C++题解:树的直径_c++树的直径-CSDN博客

                                                            

#include <iostream>
#include <algorithm>
//无环图 首都到其他仅(不重复)一条路 
#include <vector>

using namespace std;

typedef long long LL;

const int N=1e5+10;

struct Node
{
  int id,w;  
};

vector<Node> node[N];

int n;
int dist[N];

void dfs(int u,int father,int distance)//u:此点 
{
    dist[u]=distance; //求dist 
    
    //求各点到该点u的距离
    for(auto nd:node[u]) //u->nd 
        if(nd.id!=father) //换点了,可 
            dfs(nd.id,u,dist[u]+nd.w);//顺着树节点往下走 
}

int main()
{
    cin>>n;
    
    for(int i=0;i<n-1;i++)
    {
        int p,q,d;
        cin>>p>>q>>d;
        
        node[p].push_back({q,d});//node[i].id:i连向的点 
        node[q].push_back({p,d});
    }
    
    /*任意取一点,计算其他点距离该点的最远距离
    这里的任意一点取的是,树根
    
    1表示从第一个节点开始
    -1表示该结点的前一个结点(父节点)为-1,即无
    0表示目前该结点到任意取得结点的距离,初始化为0*/
    dfs(1,-1,0);
    
    //找到距离根节点最远的叶子节点
    int u=1;
    for(int i=1;i<=n;i++)
        if(dist[i]>dist[u])  //dist最大 
            u=i;
            
    //dfs:其他结点距离该叶子节点的距离
    dfs(u,-1,0);//初始化 
    
    for(int i=1;i<=n;i++)
        if(dist[i]>dist[u])//dist最大 
            u=i;
            
    cout<<(10*dist[u]+dist[u]*(dist[u]+1LL)/2)<<endl;
    
    return 0;
}

spfa

作物杂交

//https://www.acwing.com/video/4647/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
//h:邻接表  dist[x]迄今为止生成x需要的时间  w[x]:一个x成熟的时间

using namespace std;

const int N = 2010, M = 200010;

int n, m;
int h[N], e[M], w[N], target[M], ne[M], idx;
int dist[N];
queue<int> q;
bool st[N];
void add(int a, int b, int c)
{
    e[idx] = b, target[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void spfa()
{
    while (q.size())
    {
        int x = q.front();
        q.pop();
        st[x] = false;//标记节点x不在队列中
        for (int i = h[x]; i != -1; i = ne[i])//可扩展min
        {
            int y = e[i], z = target[i];
            if (dist[z] > max(dist[x], dist[y]) + max(w[x], w[y]))
            {
                dist[z] = max(dist[x], dist[y]) + max(w[x], w[y]);
                if (!st[z])//节点z不在队列中
                {
                    q.push(z);//加入队列
                    st[z] = true;
                }
            }
        }
    }
}
int main()
{
    int k, T;
    scanf("%d%d%d%d", &n, &m, &k, &T);
    memset(h, -1, sizeof h);//
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);//从1
    memset(dist, 0x3f, sizeof dist);//
    while (m -- )
    {
        int x;
        scanf("%d", &x);
        dist[x] = 0;
        q.push(x);
        st[x] = true;
    }
    while (k -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
        add(b, a, c);
    }
    spfa();
    printf("%d\n", dist[T]);
    return 0;
}

 dp

砝码称重

AcWing 3417. 砝码称重(闫式dp/dfs) - AcWing 题解

/*每个砝码 不放 放左 右
*f[i][j]从前i个砝码中选 称重量为j 是否可以
*/
#include<iostream>
using namespace std;
const int N=1e5+10;
bool f[110][N];
int a[N];
int main()
{
    f[0][0]=true;
    int n;
    cin>>n;
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=sum;j++)//从0开始   方案数
        {
            f[i][j]=f[i-1][j]||f[i-1][abs(j-a[i])];     //第i个 不选/左/右
        
        //防止越界
             if (j + a[i] <= sum) f[i][j] |= f[i - 1][j + a[i]];//或运算
        }
    }
    int ans=0;
    for(int i=1;i<=sum;i++) //答案中 称出重量为0的不算
    {
        if(f[n][i]==true) ans++;
    }
    cout<<ans<<endl;
}

松散子序列

/*dp:选或不选*/
#include<iostream>
#include<cstring>
//pi:不能连续选
using namespace std;
char str[1000010];
int f[1000010][2];
int main()
{
    scanf("%s",str+1);
    int n=strlen(str+1);
    
    for(int i=1;i<=n;i++)
    {
        f[i][0]=max(f[i-1][0],f[i-1][1]);//不选  
        f[i][1]=f[i-1][0]+str[i]-'a'+1;  //前一个只能不选
    }
    cout<<max(f[n][1],f[n][0]);
}

垒筛子******

地宫取宝

#include <bits/stdc++.h>

using namespace std;

const int N = 55, M = 15, mod = 1e9 + 7;

int n, m, c;
int a[N][N];
int f[N][N][M][M];

int main() {
  cin >> n >> m >> c;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      cin >> a[i][j];
      a[i][j]++;
    }
  }
  // 初始化
  f[1][1][0][0] = f[1][1][1][a[1][1]] = 1;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      for (int cnt = 0; cnt <= c; cnt++) {
        for (int k = 0; k < M; k++) {
          if (f[i][j][cnt][k]) {
            // 不取 (i+1,j) 的物品,可以直接从 (i,j) 转移到 (i+1,j)
            f[i + 1][j][cnt][k] = (f[i + 1][j][cnt][k] + f[i][j][cnt][k]) % mod;
            // 不取 (i,j+1) 的物品,可以直接从 (i,j) 转移到 (i,j+1)
            f[i][j + 1][cnt][k] = (f[i][j + 1][cnt][k] + f[i][j][cnt][k]) % mod;
            // 还可以取物品
            if (cnt + 1 <= c) {
              // 取 (i+1,j) 的物品,从 (i,j) 转移到 (i+1,j)
              if (a[i + 1][j] > k) {
                f[i + 1][j][cnt + 1][a[i + 1][j]] = (f[i + 1][j][cnt + 1][a[i + 1][j]] + f[i][j][cnt][k]) % mod;
              }
              // 取 (i,j+1) 的物品,从 (i,j) 转移到 (i,j+1)
              if (a[i][j + 1] > k) {
                f[i][j + 1][cnt + 1][a[i][j + 1]] = (f[i][j + 1][cnt + 1][a[i][j + 1]] + f[i][j][cnt][k]) % mod;
              }
            }
          }
        }
      }
    }
  }
  int res = 0;
  for (int i = 1; i < M; i++) {
    res = (res + f[n][m][c][i]) % mod;
  }
  cout << res << '\n';
  return 0;
}

画中漂流***

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

typedef long long LL;

const int MOD = 1000000007;
const int N = 3010;

int dp[N][N];  // dp[i][j]  : 时间i 剩余体力j 的方案数
LL res;
int d, t, m;

int main() {
    cin >> d >> t >> m;
    dp[0][m] = 1;

    // i 是时间,power 是体力
    for (int i = 1; i <= t; ++i) {
        for (int power = 0; power <= m; ++power) {//power:剩的体力
            // 通过时间和体力算出距离,用了多少体力就向上游了多少
            // 原始距离 = d
            // 当前时间 = i
            // 当前剩余体力 = power
            // 计算向上游长度 = (m - power)
            // 计算向下漂流长度 = i - (m - power)
            // 计算当前位置 = 原始距离 + 向上游长度 - 向下漂流长度
            // 计算当前位置 = d + (m - power) - (i - (m - power));

            int length = d + (m - power) - (i - (m - power));
            // 不死
            if (length > 0) {
                //第i次不用体力  i=power ->  i-1=power
                dp[i][power] = (dp[i][power] + dp[i - 1][power]) % MOD;
                //第i次用体力   i :power   -> i-1次power+1
                dp[i][power] = (dp[i][power] + dp[i - 1][power + 1]) % MOD;
            }
        }
    }
    // 要求必须要用完所有体力
    cout << dp[t][0];
    return 0;
}

数字三角形

/*Dp
   *              *                *
  /              /                  \ 
 *   *          *   *            *   *
  \              \                  / 
*  *  *        *  *  *          *  *  *
                 /                  \
             *  *  *  *        *  *  *  *  
n为偶数时,最后一层落在的点一定在n/2或n/2+1
n为奇数时,最后一层落在的点一定在n/2+1(向左下走的次数与向右下走的次数相差不能超过 1)
f[i][j]表示所有从头开始往下走到第i层第j个的路径的最大值
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 110;
int n;

int f[N][N];
int triangle[N][N];
int main()
{
    scanf("%d",&n);
    for(int i = 1; i<=n; i++)
        for(int j = 1; j <=i; j++)//j<=i i***
            scanf("%d",&triangle[i][j]);

    for(int i = 1; i<= n; i++)
        for(int j = 1; j <=i; j++)
            f[i][j] = max(f[i-1][j],f[i-1][j-1]) + triangle[i][j];//max   +
    if(n % 2 == 0) printf("%d\n", max(f[n][n/2],f[n][n/2+1]));
    else printf("%d\n", f[n][n/2+1]);
    return 0;
}

最优包含

//注意:是操作
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
char a[1050],b[1050];
int f[1050][1050];
int main()
{
    scanf("%s",a+1);
    scanf("%s",b+1);
    int n=strlen(a+1),m=strlen(b+1);
    memset(f, 0x3f, sizeof f);//找min,先弄成最大的  顺序与下
    
    f[0][0]=0;
     
    for(int i=1;i<=n;i++)
    {
        f[i][0]=0;
        for(int j=1;j<=m;j++)
        {
            f[i][j]=f[i-1][j];//要不弄成0x3f  min一直=0
            if(a[i]==b[j]) f[i][j]=min(f[i][j],f[i-1][j-1]);//不用a[i],用a[i] 操作数
            //a[i]=b[j] 不用操作
            else f[i][j]=min(f[i][j],f[i-1][j-1]+1);//不用a[i],用a[i]      a[i]!=b[j] 用操作
        }
    }
    cout<<f[n][m];
    
}

走方格dfs

在平面上有一些二维的点阵。

这些点的编号就像二维数组的编号一样,从上到下依次为第 11 至第 n� 行,从左到右依次为第 11 至第 m� 列,每一个点可以用行号和列号来表示。

现在有个人站在第 11 行第 11 列,要走到第 n� 行第 m� 列。

只能向右或者向下走。

注意,如果行号和列数都是偶数,不能走入这一格中。

问有多少种方案。

#include<bits/stdc++.h>
using namespace std;

int f[40][40];//f[i][j]从1,1到i,j方案数
int n,m;

void dfs()
{
    f[1][1]=1;//从1,1到1,1的方案数:1(不用动的方案)

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(i==1&&j==1) continue;
            if(i%2==0&&j%2==0) continue;
            else f[i][j]=f[i-1][j]+f[i][j-1];//从上/左来
        }
    }
}
int main()
{
    cin>>n>>m;
    dfs();
    cout<<f[n][m]<<endl;
    return 0;
}

区间DP

石子合并(模板)

设有 N� 堆石子排成一排,其编号为 1,2,3,…,N1,2,3,…,�。

每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N� 堆石子合并成为一堆。

每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

例如有 44 堆石子分别为 1 3 5 2, 我们可以先合并 1、21、2 堆,代价为 44,得到 4 5 2, 又合并 1、21、2 堆,代价为 99,得到 9 2 ,再合并得到 1111,总代价为 4+9+11=244+9+11=24;

如果第二步是先合并 2、32、3 堆,则代价为 77,得到 4 7,最后一次合并代价为 1111,总代价为 4+7+11=224+7+11=22。

问题是:找出一种合理的方法,使总的代价最小,输出最小代价。

/*f(i,j):从i合并到j 需要的代价
i<k<j

f(i,j)=minf(i,k) minf(k+1,j)+数字总和

len:区间长度 j:区间右端点  i:区间左端点   
k:左边堆的右端点    f(i,j)   =左i~k  右k+1~j****

枚举长度、左端点*/

#include<iostream>
using namespace std;
const int N=400;
int f[N][N],s[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        s[i]+=s[i-1];
    }
    
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i+len-1<=n;i++)//在len 约束下的j
        {
            int j=i+len-1;
            f[i][j]=1e8;
            for(int k=i;k<=j-1;k++)//k=i:左边只有一堆  k=j-1(左边这一堆最大到j-1,右边最少有一堆)****
            {
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]); //代价的和  +号    合并左,右独立
            }
        }
    }
    cout<<f[1][n];
}

          合并石子(真题)比上面多了颜色属性

题解AcWing 5403. 合并石子 - AcWing

在桌面从左至右横向摆放着 N� 堆石子。

每一堆石子都有着相同的颜色,颜色可能是颜色 00,颜色 11 或者颜色 22 中的其中一种。

现在要对石子进行合并,规定每次只能选择位置相邻并且颜色相同的两堆石子进行合并。

合并后新堆的相对位置保持不变,新堆的石子数目为所选择的两堆石子数目之和,并且新堆石子的颜色也会发生循环式的变化。

具体来说:两堆颜色 00 的石子合并后的石子堆为颜色 11,两堆颜色 11 的石子合并后的石子堆为颜色 22,两堆颜色 22 的石子合并后的石子堆为颜色 00。

本次合并的花费为所选择的两堆石子的数目之和。

给出 N� 堆石子以及他们的初始颜色,请问最少可以将它们合并为多少堆石子?

如果有多种答案,选择其中合并总花费最小的一种,合并总花费指的是在所有的合并操作中产生的合并花费的总和。

const int N = 5e2+10,INF=1e16;
//最后一句所说的为什么不是最小的,是因为可能这个区间没法合并成一块,所以此时的值是无穷大,因此我们要在后面再跑一次dp**************
int n;
int f[N][N][3]; //f[i][j][k]表示i-j这区间合并的石子的颜色,INF表示不合法
int s[N];
int c[N][N];    //c(i,j)表示 合并 i-j这区间后 的 最小的堆数
int w[N][N];    //w(i,j)表示 合并 i-j这区间后 的 最小的价值
void solve(){
    cin>>n;  
    //初始化c数组 
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            c[i][j]=j-i+1;
            if(i!=j)w[i][j]=INF;    //初始化价值
            for(int k=0;k<3;k++)    //初始化f数组
                f[i][j][k]=INF;
        }
    }
    //前缀和存数据,区间dp常见操作,方便后面取到区间的值
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        s[i]=s[i-1]+x;
    }
    //初始化 显然当前长度为1的区间 颜色x 是合法的
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        f[i][i][x]=0;
    }
    //区间dp
    for(int len=1;len<=n;len++){            //长度
        for(int l=1;l+len-1<=n;l++){        //左端点
            int r=l+len-1;                  //右端点
            for(int i=0;i<3;i++){           //枚举颜色的所有可能
                int ans=INF;                //记录合法情况的最小值
                for(int j=l;j<r;j++){       //枚举中间的边界
                    if(f[l][j][i]!=INF&&f[j+1][r][i]!=INF){         //两边都是合法情况才允许合并
                        ans=min(ans,f[l][j][i]+f[j+1][r][i]);       //记录最小值
                    }
                }
                if(ans!=INF){           //不为INF代表有合法情况
                    c[l][r]=1;          //此时区间长度为1,因为变成一堆石子
                    f[l][r][(i+1)%3]=min(f[l][r][(i+1)%3],ans+s[r]-s[l-1]); //更新答案

                    //更新最小价值
                    w[l][r]=min(w[l][r],f[l][r][(i+1)%3]);     
                }
            }

            for(int j=l;j<r;j++){               //枚举中间的边界
                if(c[l][r]>c[l][j]+c[j+1][r]){  //找到更小合并方式就更新 堆数 和 价值
                    c[l][r]=c[l][j]+c[j+1][r];
                    w[l][r]=w[l][j]+w[j+1][r];
                }
                else if(c[l][r]==c[l][j]+c[j+1][r]){        //相等的情况 就 选价值最小的
                    w[l][r]=min(w[l][r],w[l][j]+w[j+1][r]);
                }
            }
        }
    }
    cout<<c[1][n]<<' '<<w[1][n];
}
signed main(){
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
    int t=1;
    // cin>>t;
    while(t--){
        solve();
    }
}

合并果子(贪心)(haffman树)

/*q.top(),q.push() */
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int main()
{
    int n;
    cin>>n;
    priority_queue<int,vector<int>,greater<int>> q;
    for(int i=0;i<n;i++)
    {
        int x;
        cin>>x;
        q.push(x);
    }
    long long res=0;
    while(q.size()>1)
    {
        int a=q.top(); q.pop();
        int b=q.top(); q.pop();
        res+=a+b;
        q.push(a+b);
    }
    printf("%lld\n",res);
    return 0;
}

数组切分

已知一个长度为 N� 的数组:A1,A2,A3,…AN�1,�2,�3,…�� 恰好是 1∼N1∼� 的一个排列。

现在要求你将 A� 数组切分成若干个 (最少一个,最多 N� 个) 连续的子数组,并且每个子数组中包含的整数恰好可以组成一段连续的自然数。

例如对于 A={1,3,2,4}�={1,3,2,4},一共有 55 种切分方法:

  • {1}{3}{2}{4}{1}{3}{2}{4}:每个单独的数显然是 (长度为 11 的) 一段连续的自然数
  • {1}{3,2}{4}{1}{3,2}{4}:{3,2}{3,2} 包含 22 到 33,是一段连续的自然数,另外 {1}{1} 和 {4}{4} 显然也是。
  • {1}{3,2,4}{1}{3,2,4}:{3,2,4}{3,2,4} 包含 22 到 44,是一段连续的自然数,另外 {1}{1} 显然也是。
  • {1,3,2}{4}{1,3,2}{4}:{1,3,2}{1,3,2} 包含 11 到 33,是一段连续的自然数,另外 {4}{4} 显然也是。
  • {1,3,2,4}{1,3,2,4}:只有一个子数组,包含 11 到 44,是一段连续的自然数

题解: AcWing 4668. 数组切分 - AcWing

//4 3 2 1
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 10010, mod = 1000000007;

int n;
int a[N];
int f[N];

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	
	f[0]=1;
	
	for(int i=1;i<=n;i++)
	{
		int Max=a[i],Min=a[i];
		for(int j=i;j>=1;j--)
		{
			Min=min(Min,a[j]);
			Max=max(Max,a[j]);
			
			//连续自然数 
			if((i-j)==(Max-Min))
			f[i]=(f[i]+f[j-1])%mod;//f[i]方案数由各种f[j-1]相加组成 
		}
	}
	cout<<f[n];
	
}

糖果

蓝桥杯.糖果(状压dp)_糖果 蓝桥杯-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_59700927/article/details/123118678

糖果店的老板一共有 M� 种口味的糖果出售。

为了方便描述,我们将 M� 种口味编号 1∼M1∼�。

小明希望能品尝到所有口味的糖果。

遗憾的是老板并不单独出售糖果,而是 K� 颗一包整包出售。

幸好糖果包装上注明了其中 K� 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。

给定 N� 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。

第一行包含三个整数 N,M,K�,�,�。

接下来 N� 行每行 K� 个整数 T1,T2,⋅⋅⋅,TK�1,�2,···,��,代表一包糖果的口味。

一个整数表示答案。

如果小明无法品尝所有口味,输出 −1−1。


#include <bits/stdc++.h>
using namespace std;
int n, m, k, x;
int a[110]; //袋子
int dp[1<<21];//糖果情况 二进制
int main(void)
{
    cin >>n >>m >>k;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= k; j++){
            //读入每包糖果 
			cin >>x;
            a[i] |= 1 << (x - 1);
       //x是几,位置:几-1  位置和进制关系  位置1,2的0进制  多1的关系 
        }
    }
    //dp初始化 
    //因为是不断刷新最小值,所以初始化应该是最大值 
    memset(dp, 127, sizeof(dp));
    dp[0] = 0;
    
    for(int i = 1; i <= n; i++)//枚举袋子找min
    {
        for(int j = 0; j <=  (1 << m)-1 ; j++)//dp[i]有i种糖果需要的袋数  j:某串二进制 
        //(1<<m) -1 :111111
        {
            //如果最小次数已经大于n,跳过 
			if(dp[j] > n) continue;
            //将原来得到糖果所需的次数与当前次数比较并赋值 
			dp[j | a[i]] = min(dp[j | a[i]], dp[j] + 1);//a[i]:每包情况  j|a[i]:原来已经的二进制|刚拿的一个对应袋子里糖果数二进制==现在
        }
    }
    
    
    //如果拿到所有糖果((1<<m)-1)所需次数大于n(其实应该是因为某种糖果没有),输出-1 
    if(dp[(1<<m)-1] > n) cout <<-1;//1<<m -1 :111111
    else cout <<dp[(1<<m)-1];
    return 0;
}

李白打酒

AcWing 4408. 2022第十三届蓝桥杯 C++/C B组 李白打酒加强版 (超级详细版) - AcWing

f[n-1][m][1]:最后一次正好喝完酒 喝1斤,所以前一次正好剩下1斤

#include<bits/stdc++.h>
using namespace std;

const int N = 110, M = 1000000007;
int n, m, f[N][N][N];

int main()
{
    scanf("%d%d", &m, &n);

    for(int i = 0; i <= n; i ++ )
    {
        for(int j = 0; j <= m; j ++ )
        {
            for(int k = 0; k < N; k ++ )
            {
                if(i == 0 && j == 0 && k == 2)
                f[i][j][k] = 1;

                if(i == 0 && j == 0)
                continue;

                if(i > 0)
                f[i][j][k] = (f[i][j][k] + f[i - 1][j][k + 1]) % M;

                if(j > 0 && k % 2 == 0)
                f[i][j][k] = (f[i][j][k] + f[i][j - 1][k / 2]) % M;
            }
        } 
    }

    printf("%d", f[n - 1][m][1]);
    return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值