1、签到题

不解释

2、序列2

应该也不难吧 没代码

3、数字串

题解:n的范围是1≤n≤1000,所以这道题直接预处理前1000个数就行。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int a[N],b[N];
int main()
{
    int tt;
    cin>>tt;
    int t=1;
    for(int i=1;t<N;i++)
    {
        int sum=i,p=0;
        while(sum) b[p++]=sum%10,sum/=10;
        for(int j=p-1;j>=0;j--) a[t++]=b[j];
    }
  
    while(tt--)
    {
        int n;
        cin>>n;
        cout<<a[n]<<endl;
    }
    return 0;
}

4、四则运算

这里有一个我自己写的特别长的代码,大家可以参考,也可以百度


//计算表达式
#include<cstring>
#include<iostream>
using namespace std;
char s[100];
int numb[100];
char f[100];
int k=0,k1=0;
void trans()
{
    for(int i=0;i<strlen(s);i++)
    {
        if(s[i]<='9'&&s[i]>='0')
        {
            numb[k]=numb[k]*10+s[i]-'0';
        }
        else{
            f[k1++]=s[i];
            k++;
        }
    }
}
int main(){
    cin>>s;
    int sum;
    trans();
    for(int i=0;i<k1;i++)
    {
        if(f[i]=='*')
        {
            numb[i]*=numb[i+1];
            for(int j=i+1;j<k;j++)
                numb[j]=numb[j+1];
            for(int j=i;j<k1;j++)
                f[j]=f[j+1];
            k1--;
            k--;
        }
        if(f[i]=='/')
        {
            numb[i]/=numb[i+1];
            for(int j=i+1;j<k;j++)
                numb[j]=numb[j+1];
            for(int j=i;j<k1;j++)
                f[j]=f[j+1];
            k1--;
            k--;
        }
    }
    for(int i=0;i<k1;)
    {
        if(f[i]=='+')
        {
            numb[i]+=numb[i+1];
            for(int j=i+1;j<k;j++)
                numb[j]=numb[j+1];
            for(int j=i;j<k1;j++)
                f[j]=f[j+1];
            k1--;
            k--;
        }
        if(f[i]=='-')
        {
            numb[i]-=numb[i+1];
            for(int j=i+1;j<k;j++)
                numb[j]=numb[j+1];
            for(int j=i;j<k1;j++)
                f[j]=f[j+1];
            k1--;
            k--;
        }
    }
    cout<<numb[0]<<endl;
}

5、亲子鉴定s

字符串比较,可以先把所有字母变大小写再strcmp

#include<stdio.h>
#include<string.h>
int main()
{
	int i=0;
	char s1[2000],s2[2000];
	scanf("%s",s1);
	scanf("%s",s2);
	while(s1[i]!='\0')
	{
		if(s1[i]>=65&&s1[i]<=90)
			s1[i]+=32;
		i++;
	}
	i=0;
		while(s2[i]!='\0')
	{
		if(s2[i]>=65&&s2[i]<=90)
			s2[i]+=32;
		i++;
	}
	i=0;
	while(s1[i]==s2[i]&&s1[i]!='\0')
	i++;
	if(s1[i]>s2[i])
 		printf("%d",1);
	else if(s1[i]<s2[i])
		printf("%d",-1);
	else if(s1[i]==s2[i])
	 	printf("%d",0);
	return 0;
}

6、协会解散

这个就是概率题

#include <bits/stdc++.h>
using namespace std;
int t;
double p;
int main(){
    scanf ("%d", &t);
    while (t --){
        scanf ("%lf", &p);
        printf ("%.6lf\n", min(1.0 * p / (1 - p), 1.0));
    }
    return 0;
}

7、有趣的定理

//cf304 A
//2013-06-05-18.14
#include <stdio.h>
#include <math.h>
int main()
{
    int n;
    while (scanf("%d", &n) != EOF)
    {
        int cnt = 0;
        for (int i = 1; i < n; i++)
        {
            for (int j = 1; j < i; j++)
            {
                int t = i*i + j*j;
                int c = (int)sqrt(t);
                if (c > n)
                    continue;
                if (c*c != t)
                    continue;
                if (i+j > c && i+c > j && j+c > i && i-j < c && i-c < j && j-c < i)
                    cnt++;
            }
        }
        printf("%d\n", cnt);
    }
    return 0;
}

8、仙女的游戏

前缀和

#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
const int N = 1e5 + 50;
int n, m, l, t;
int v[N];
ll s[N];
int main()
{
    cin >> n >> m;
    for(int i = 1 ; i <= n; i ++ ){
        cin >> v[i];
        s[i] += s[i - 1] + v[i];
    }
    for(int i = 1; i <= m; i ++ ){
        cin >> l >> t;
        cout << s[t] - s[l - 1] << endl;
    }
    return 0;
}

9、表现字符

鸽笼原理:

字符串 s 的所有长度不小于 k 的子串都包含字符 c 相当于长度为 k的子串都包含字符 c,因为长度大于 k 的子串肯定包含了长度为 k 的子串,不妨枚举每一个字母在字符串中出现的位置,任意地,设其出现的相邻位置为 (i,j)(包括头和尾),为了使该字母在所有的长度为 k 的子串中都出现,k至少为 j−i 的最大值,否则会存在一个相邻的位置 (i,j),中间存在一个子串不包括该字母,对所有这样的字母取min即可

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010, M = 26;
int n;
char str[N];
int last[M], d[M];
int main()
{
    scanf("%s", str + 1);
    n = strlen(str + 1);
    for (int i = 1; i <= n; i ++ )
    {
        int x = str[i] - 'a';  //把每个字符都映射到为0-25
        d[x] = max(d[x], i - last[x]);//计算字符x+'a'的最大间距,last[x]为上一次出现的下标
        last[x] = i;  //记录字符x+'a'的位置
    }

    for (int i = 0; i < 26; i ++ )
        d[i] = max(d[i], n + 1 - last[i]);//处理字符i+'a'最后出现的位置到结束的长度

    int res = N + 1;
    for (int i = 0; i < 26; i ++ )
        res = min(res, d[i]); //取26个字母中间距最小的那位

    printf("%d\n", res);
    return 0;
}

10、01串

DP+前缀和

思路1:

状态:dp [i] [0] 表示字符串第i位为0的优秀字符串的个数

​ 状态计算:

dp[i][0]=d[i1][0]+dp[i1][1]dp[i][0]=d[i-1][0]+dp[i-1][1]

​ dp [i] [1] 表示字符串第i位为1的优秀字符串的个数

dp[i][1]=d[ik][0]+dp[ik][1]dp[i][1]=d[i-k][0]+dp[i-k][1]

那么长度为i的游戏字符串个数即为dp [i] [0] + dp [i] [1]

前缀和数组f[i]累加长度小于等于i的个数之和

[l,r]范围内的所有 01 字符串中优秀字符串的数量即为:f[r]-f[l-1]

复杂度:(O(n))

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110000,mod=1e9+7;

int t,k;
int dp[N][2],f[N];

int main()
{
    cin>>t>>k;
    for(int i=0;i<k;i++) dp[i][0]=1,f[i]=i;
    
    for(int i=k;i<=100010;i++)
    {
        dp[i][0]=(dp[i-1][0]+dp[i-1][1])%mod;
        dp[i][1]=(dp[i-k][0]+dp[i-k][1])%mod;
        f[i]=((f[i-1]+dp[i][0])%mod+dp[i][1])%mod; //前缀和数组
    }
    while(t--)
    {
        int l,r;
        cin>>l>>r;
        cout<<(f[r]-f[l-1]+mod)%mod<<endl;
    }
    return 0;
}

思路2:

状态:dp[i] 表示所有长度为i的游戏字符串

dp[i]=dp[i1]+dp[ik];dp[i]=dp[i-1]+dp[i-k];

考虑第i位是0时,表示第i位不影响前面i-1位,即可直接由dp[i-1]转换过来

考虑第i位是1时,表示最近的k位都必须是1,即i-k+1到i这k位都已经确定(必须为1),即前i-k位不被影响,可由dp[k-1]转换过来。

前缀和数组f[i]累加长度小于等于i的优秀字符串个数之和

[l,r]范围内的所有 01 字符串中优秀字符串的数量即为:f[r]-f[l-1]

复杂度:(O(n))

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010, mod = 1e9 + 7;

int t, k;
int dp[N];
int f[N];

int main()
{
    cin>>t>>k;
    dp[0] = 1;
    for (int i = 1; i < N; i ++ )
    {
        dp[i] = dp[i - 1]; //i位为0
        if (i >= k) dp[i] = (dp[i] + dp[i - k]) % mod;//i位为1
    }
    for (int i = 1; i < N; i ++ ) f[i] = (dp[i] + f[i - 1]) % mod;//求前缀和

    while (t -- )
    {
        int l, r;
        cin>>l>>r;
        cout<<(f[r]-f[l-1]+mod)%mod<<endl;
    }

    return 0;
}

11、CDTU运动会

#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define me memset
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
using namespace std;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
ll a[2010];
ll dp[2010][2010];
int main()
{
    int n;
    cin>>n;
    for(int i=1 ; i<=n ; i++) cin>>a[i];
    sort(a+1,a+1+n);
    for(int len=1 ; len<=n ; len++)
    {
        for(int j=1 ; j+len<=n ; j++)
        {
            int l=j,r=j+len;
            dp[l][r]=min(dp[l][r-1],dp[l+1][r])+(a[r]-a[l]);
        }
    }
    cout<<dp[1][n]<<endl;
    return 0;
}

12、路径权值最大

前置知识: 1、拓扑图和拓扑排序

2、DAG 图定义:如果一个有向图无法从任意顶点出发经过若干条边回到该点,则这个图就是有向无环图。所以一个 DAG 图是没有自环和回路的。

题目分析: 如果题目中存在自环和回路,环上所有点的字母都会出现无穷多次,就不可能存在出现次数最多的情况。

若没有自环和回路,路径长度一定是有限值,一定有解。

路径长度最大为 n

如何判断有无环存在: 常用方法:

1、Topsort

2、有向图的强连通分量

如果不存在环,怎么求最长路径? 每个点会分配一个小写字母,出现次数最多的字母,一共只有 26 种,而每个点的权值只有 26 种,可以考虑枚举每个字母为最大值的情况,最后求一个max。

以a为例

如果一个点的字母是a,则点权为 1,否则为 0。

a>b>a>c>a1>0>1>0>1DPa - > b - > a -> c -> a\\ 1 -> 0 -> 1 -> 0 -> 1 即在这个拓扑图上求最长路,可以用递推和 DP 来求,

即在这个拓扑图上求最长路,可以用递推和 DP 来求,

对于已经求好的拓扑序列,倒序使用递推求最长路即可。

这里稍微解释一下为什么可以这么做:

首先要理解一下拓扑序列的含义,这里我们可以使用拓扑排序的方式来判断该图是否是 DAG 图,如果是,则队列q[]中存放的就是一个拓扑序列,那么我们可以从后往前倒推,用闫氏 DP 分析法来分析:

闫氏 DP 分析法: 状态表示(在拓扑序列中):

集合:f[t] 表示所有以 t 为起点的路径构成的集合 属性:集合中所包含路径长度的最大值

状态计算:

f[t]=max(f[t],Wtj+f[j]),jAAtf[t]=max(f[t],Wt→j+f[j]), j∈A,其中 A 表示点 t 的所有邻边构成的集合。

这样,我们在拓扑序列中从后往前递推,就可以算出权值最大的路径和最大路径权值了。

时间复杂度: O(26⋅n)。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 300010, M = N;
int n, m;
char w[N]; //每个点对应的字母
int h[N], e[M], ne[M], idx;//领接表
int q[N], d[N];//q[N]存topsort数组,d[N]存每个点的入度
int f[N]; // 每个点的最长路
void add(int a, int b)  
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool topsort()
{
    int hh = 0, tt = -1;
    for (int i = 1; i <= n; i ++ )
        if (!d[i]) 
            q[ ++ tt] = i;   //先把所有入度为0的点加入
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; ~i; i = ne[i])  //遍历每个点的出边
        {
            int j = e[i];
            -- d[j];
            if (!d[j])  q[ ++ tt] = j;   //存入入度变为0的点
        }
    }
    return tt == n - 1; //如果n个点都存入,说明有topsort序列,图中没有环
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    scanf("%s", w + 1); // 读入每个点的字母
    while (m -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);//加边
        ++ d[b];//加入度
    }
    if (!topsort()) puts("-1");
    else
    {
        int res = 0; // 答案,最大值
        for (char c = 'a'; c <= 'z'; c ++ )
            for (int i = n - 1; i >= 0; i -- )
            {
                // q[i]为当前点
                int t = q[i]; 
                // 看一下当前这个点权值为多少。
                int v = w[t] == c;
                // f[t] 表示以t为起点的长度的最大值
                f[t] = v;
                // t不一定有后继,如果有后继,则遍历所有t的出边
                for (int j = h[t]; ~j; j = ne[j])
                {
                    int k = e[j];
                    f[t] = max(f[t], v + f[k]);
                }
                res = max(res, f[t]);
            }
        printf("%d\n", res);
    }
    return 0;
}