首页 > 其他分享 >《看了受制了》第三十九天,14题,合计224道题

《看了受制了》第三十九天,14题,合计224道题

时间:2023-10-10 23:55:24浏览次数:45  
标签:道题 14 int res void 第三十九 cin max 序列

2023年10月10日

1. Acwing1015 摘花生

题目理解

状态表示:f[i][j]表示,走到f[i][j]的方法的所有的集合。

集合属性:最大值

状态转移:f[i][j] += max(f[i - 1][j], f[i][j - 1])(因为只能从上面和左面过来)

代码实现

// 两种可能,从上面来和从左面来

// 集合表示是:走到i,j这个格子的集合
// 属性是: 最大值
// 所以d[i][j] = max(d[i][j] + d[i - 1][j], d[i][j] + d[i][j - 1]);
const int N = 110;
int d[N][N];
void solve()
{
	memset(d, 0, sizeof d);
	int n, m;
	cin >> n >> m;

	for(int i = 1; i <= n; i++)
	    for(int j = 1; j <= m; j++)
	        cin >> d[i][j];

	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
			d[i][j] = max(d[i][j] + d[i - 1][j], d[i][j] + d[i][j - 1]);
	}

	cout << d[n][m] << endl;
	return;
}

2. Acwing1018 最低通行费

题目理解

状态表示:f[i][j]表示,走到f[i][j]的方法的所有的集合。

集合属性:最小值

状态转移:

  • i != 1 && j != 1

f[i][j] += max(f[i - 1][j], f[i][j - 1])(因为只能从上面和左面过来)

  • i == 1

f[i][j] = f[i][j - 1](因为第一排只能从左边来)

  • j == 1

f[i][j] = f[i - 1][j](因为只能从上面来)

代码实现

// f[i][j] 表示走到 i、j格子的方案集合
// 属性是min

// d[i][j] = min(d[i][j] + d[i - 1][j], d[i][j] + d[i][j - 1])
// 需要处理一下边界
// 如果是第一行的,只能从左边来
// 如果是第一列的, 只能从上面来
const int N = 110;
int d[N][N], f[N][N];
void solve()
{
	memset(f, INF, sizeof f);
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			cin >> d[i][j];

	// 根据定义d[0][j]应该为0
	for(int i = 0; i <= n; i++) 
		d[0][i] = d[i][0] = 0;

	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
		{
			if(i != 1 && j != 1)
				d[i][j] = min(d[i][j] + d[i - 1][j], d[i][j] + d[i][j - 1]);
			else if(i == 1)
				d[i][j] += d[i][j - 1];
			else if(j == 1)
				d[i][j] += d[i - 1][j];
		}

	cout << d[n][n];
	return;
}

3. Acwing1027 方格取数

题目理解

通常我们比较容易想到的是\(f[i1][j1][i2][j2]\)去代表\((1,1)(1,1)开始到(i1, j1)(i2, j2)\)两条路线的最大值,那么最后的最大就是\(f[n][n][n][n]\)在这个模型的基础上我们可以优化一下。为以下:

用一维来枚举当前是第几步\(f[k][i1][i2]\)
什么含义呢?就是走了\(k\)步,我第一条走到了\(i1\)行,第二条走到了\(i2\)行
需要特判的是。因为步数是一定的所以我们的列数可以通过\(k - i\)获得到目前的列数。
但是切记我们的格子只可以走一次,所以我们当两条路同时走到一个格子的时候可不能加两次需要特判。
就是当\(i1 == i2\)的时候。

状态表示:f[k][i][j]表示用了k步走到了第一条走到了第i行和第二条走到了第j

集合属性:最大值

状态转移:

int t = w[i1][j1];
if(i1 != i2) t += w[i2][j2];
int &x = f[k][i1][i2];

x = max(x, f[k-1][i1-1][i2-1] + t);
x = max(x, f[k-1][i1-1][i2] + t);
x = max(x, f[k-1][i1][i2-1] + t);
x = max(x, f[k-1][i1][i2] + t);

代码实现

const int N = 15;
int f[N * 2][N][N], d[N][N];
int n;

void solve()
{

	cin >> n;
	int a, b, c;
	while(cin >> a >> b >> c){
		if(a == 0 && b == 0 && c == 0) break;
		d[a][b] = c;
	}

	for(int k = 2; k <= n * 2; k++)		// 走了k步
		for(int x1 = 1; x1 <= n; x1++)
			for(int x2 = 1; x2 <= n; x2++)
			{
				int y1= k - x1, y2 = k - x2;

				if(y1 >= 1 && y1 <= n && y2 >= 1 && y2 <= n)	// 在范围内
				{
					int t = d[x1][y1];
					if(x1 != x2) t += d[x2][y2];

					int &x = f[k][x1][x2];
					x = max(x, f[k - 1][x1 - 1][x2 - 1] + t);
					x = max(x, f[k - 1][x1 - 1][x2] + t);
					x = max(x, f[k - 1][x1][x2] + t);
					x = max(x, f[k - 1][x1][x2 - 1] + t);
				}
			}

	cout << f[n * 2][n][n];
	return;
}

4. Acwing275 传纸条

题目理解

这个题目本质上就和上面的方格取数一样了,我们可以把从AB看作是,第一条路;从BA看作是第二条路即可。

代码实现

const int N = 55;
int w[N][N], f[N * 2][N][N];
int n, m;
void solve()
{

	cin >> n >> m;

	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			cin >> w[i][j];

	for(int k = 2; k <= n + m; k++)
		for(int x1 = 1; x1 <= n; x1++)
			for(int x2 = 1; x2 <= m; x2++)
			{
				int y1 = k - x1, y2 = k - x2;

				if(y1 <= n && y1 >= 1 && y2 >= 1 && y2 <= m)
				{
					int t = w[x1][y1];
					if(x1 != x2) t += w[x2][y2];	// 如果不是同一个格子就加起来

					int &x = f[k][x1][x2];

					x = max(x, f[k - 1][x1 - 1][x2 - 1] + t);
					x = max(x, f[k - 1][x1 - 1][x2] + t);
					x = max(x, f[k - 1][x1][x2 - 1] + t);
					x = max(x, f[k - 1][x1][x2] + t);
				}
			}

	cout << f[n + m][n][m];
	return;
}

5. Acwing895 最长上升子序列模型Ⅰ

题目理解

状态表示:f[i]表示以i结尾的最长上升子序列的长度

集合属性:最大值

状态转移:如果\(a[i] > a[j]\)的话存在\(f[i] = max(f[i], f[j] + 1)\)

代码实现

const int N = 1010;
int f[N], a[N];
int n;
void solve()
{
	cin >> n;

	for(int i = 1; i <= n; i++) cin >> a[i];
	int res = 0;
	for(int i = 1; i <= n; i++)
	{
		f[i] = 1;
		for(int j = 1; j <= i; j++)
			if(a[i] > a[j])
				f[i] = max(f[i], f[j] + 1);

		res = max(res, f[i]);
	}

	cout << res;
	return;
}

6. Acwing896 最长上升子序列模型Ⅱ

题目理解

用贪心的方法来解:其实就是找,当前的数,是前面的序列中,第几小的数那么最长上升子序列也就是这个。

代码实现

int a[N], p[N];
int n;
void solve()
{
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	int len = 1;

	for(int i = 1; i <= n; i++)
	{
		int l = 1, r = len;
		while(l < r)
		{
			int mid = (l + r + 1) >> 1;
			if(p[mid] < a[i]) l = mid;
			else r = mid - 1;
		}
		len = max(len, r + 1);
		p[r + 1] = a[i];
	}

	cout << len - 1;

	return;
}

7. Acwing1017 怪盗基德的滑翔翼

题目理解

就是求一个数列的最长上升子序列问题。

用我们的\(f[i]\)代表的是,以第i位结尾的最长的子序列长度。

我们这里的怪盗基德是什么思路呢?

我们把它的这个分成两类
从左往右变为,最长上升子序列。
从右往左变为,最长下降子序列。(但是我们再反一下,其实就是一个最长上升子序列)

所以我们这里只需要把楼房的高度正着存一次,然后再倒着存一次,对两个数组同时做 最长上升子序列

代码实现

const int N = 110;
int a[N], n, d[N];
void solve()
{
	memset(d, 0, sizeof d);
	cin >> n;

	for(int i = 1; i <= n; i++) cin >> a[i];

	int res = 0;
	
	for(int i = 1; i <= n; i++)	// 正的求一次
	{
		d[i] = 1;
		for(int j = 1; j <= i; j++)
			if(a[j] < a[i])
				d[i] = max(d[i], d[j] + 1);
		res = max(res, d[i]);
	}

	memset(d, 0, sizeof d);

	for(int i = n; i >= 1; i--)	// 反的求一次
	{
		d[i] = 1;
		for(int j = n; j >= i; j--)
			if(a[j] < a[i])
				d[i] = max(d[i], d[j] + 1);

		res = max(res, d[i]);
	}
	cout << res << endl;
	return;
}

8. Acwing1014 登山

题目理解

要求出来,在一个序列中从左到右严格上升的子序列长度。我们这个题要的东西是,可以再这个序列中任意选一个点实现这样一个规律。
\(T1<…< Ti > Ti+1 >… > TK(1≤i≤K)\)
然后这个序列长度是最长的。所以我们只需要求出从左到T的子序列长度 + 从右到T的下降子序列长度这二者求和的最大值就是哦我们想要的答案。

代码实现

const int N = 1010;
int a[N], d[N], p[N];
void solve()
{
	int n;
	cin >> n;

	for(int i = 1; i <= n; i++) cin >> a[i];

	int res = 0;

	for(int i = 1; i <= n; i++)
	{
		d[i] = 1;
		for(int j = 1; j <= i; j++)
			if(a[i] > a[j])
				d[i] = max(d[i], d[j] + 1);
	}

	for(int i = n; i >= 1; i--)
	{
		p[i] = 1;
		for(int j = n; j >= i; j--)
			if(a[i] > a[j])
				p[i] = max(p[i], p[j] + 1);

		res = max(res, p[i] + d[i]);
	}

	cout << res - 1;	// 因为选择的山算了两次
	return;
}

9. Acwing482 合唱队形

题目理解

这个题思路同上,只是最后的答案并不是长度,而是n - 长度

代码实现

const int N = 110;
int a[N], n, d[N], p[N], res;
void solve()
{

	cin >> n;

	for(int i = 1; i <= n; i++) cin >> a[i];

	for(int i = 1; i <= n; i++)
	{
		d[i] = 1;

		for(int j = 1; j <= i; j++)
			if(a[i] > a[j])
				d[i] = max(d[i], d[j] + 1);
	}

	for(int i = n; i >= 1; i--)
	{
		p[i] = 1;
		for(int j = n; j >= i; j--)
			if(a[i] > a[j])
				p[i] = max(p[i], p[j] + 1);

		res = max(res, p[i] + d[i]);
	}

	cout << n - (res - 1);
	return;
}

10. Acwing1012 友好城市

题目理解

但是这个题的难点在于,可以抽象出这个最长上升子序列模型

1.png

题目重点思路,如何建桥可以不交叉当我们把下半部分排序,得到一个序列后。
如果我们取上半部分的单调上升子序列发现一定一定不会产生交叉!
因为,下半部分是顺序排序,这时我们取上部分的单调上升子序列的话一定不会产生交叉。真的很难想到。
就是当我们只选择最长上升子序列建立点的时候,必定是符合题意得。

说人话就是排序后,因为我是升序的,为了不交叉我也只能选择链接升序的序列

代码实现

const int N = 5010;
int n, res, d[N];
void solve()
{
	cin >> n;

	vector<PII> a(n);

	for(int i = 0; i < n; i++) 
		cin >> a[i].x >> a[i].y;

	sort(a.begin(), a.end());

	for(int i = 0; i < n; i++){
		d[i] = 1;
		for(int j = 0; j < i; j++)
			if(a[i].y > a[j].y)
				d[i] = max(d[i], d[j] + 1);
		res = max(res, d[i]);
	}
	cout << res;
	return;
}

11. Acwing1016 最长上升子序列和

题目理解

这个就比较好想了。

状态表示:f[i]表示以i结尾的最长上升子序列的和

集合属性:最大值

状态转移:如果\(a[i] > a[j]\)的话存在\(d[i] = max(d[i], a[i] + d[j]);\)

代码实现

const int N = 1010;
int a[N], res, n, d[N];
void solve()
{

	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];

	for(int i = 1; i <= n; i++)
	{
	    d[i] = a[i];
		for(int j = 1; j <= i; j++)
			if(a[i] > a[j])
				d[i] = max(d[i], a[i] + d[j]);
		
		res = max(res, d[i]);
	}

	cout << res;
	return;
}

12. Acwing1010 拦截导弹。本题得出结论:序列覆盖问题

题目理解

对于第一问:

翻译过来就是,一门炮最多拦截几枚,那么就是最长不升子序列的长度。
所以直接求一次就好了

对于第二问我们要考虑一下

题目翻译:
翻译过来是,用下降子序列,多少个下降子序列可以把我们的整个数列布满。
然后对于我们这个问题与。最长上升子序列个数是对偶的。就是我们这个的答案等于最长上升子序列。
01.png

我们从头到尾扫描一次

  • 第一种情况是当前这个数是所有子序列结尾的都比他小,就要新开序列
  • 第二种是在某一个序列中就已经比它大了,就替换一下
for(int i = 0; i < n; i++)
    {
        int k = 0;
        //从头往后扫描目前的子序列结尾
        while(k < cnt && p[k] < a[i]) k++;
        //如果目前子序列的结尾都比这个数要小,我就新开一个序列
        if(k == cnt)p[cnt++] = a[i];
        else p[k] = a[i];       //如果这个数比某个自诩列的结尾要小了,就把这个结尾替换
    }

代码实现

贪心版本:

int main()
{
    while(cin >> a[n])n++;
    int res  =0;
    //对于第一问就是求一次最长不升子序列
    for(int i = 0; i < n; i++)
    {
        f[i] = 1;
        for(int j = 0; j < i ;j++)
        {
            if(a[i] <= a[j])
                f[i] = max(f[i], f[j] + 1);
        }
        res = max(res, f[i]);
    }
    cout<<res<<endl;
    //对于第二问是贪心思路。(结论等价于求最长上升子序列)
    for(int i = 0; i < n; i++)
    {
        int k = 0;
        //从头往后扫描目前的子序列结尾
        while(k < cnt && p[k] < a[i]) k++;
        //如果目前子序列的结尾都比这个数要小,我就新开一个序列
        if(k == cnt)p[cnt++] = a[i];
        else p[k] = a[i];       //如果这个数比某个自诩列的结尾要小了,就把这个结尾替换
    }
    cout<<cnt;
    return 0;
}

结论版本:

const int N = 1010;
int a[N], d[N], p[N], res, ind;
void solve()
{
	int n = 0;
	while(cin >> a[++n]);

    
	for(int i = 1; i < n; i++)
	{
	    d[i] = 1;
	    for(int j = 1; j < i; j++)
	        if(a[i] <= a[j])
	            d[i] = max(d[i], d[j] + 1);
	   res = max(res, d[i]);
	}

	for(int i = 1; i < n; i++)
	{
		p[i] = 1;
		for(int j = 1; j <= i; j++)
			if(a[j] < a[i])
				p[i] = max(p[i], p[j] + 1);

		ind = max(p[i], ind);
	}

	cout << res << endl << ind;

	return;
}

13. Acwing187 导弹防御系统

这个题的最原本还是最长上升子序列

题目翻译过来就是用上升序列、下降序列,最少用多少个序列就可以把整个数列覆盖掉

这个题的贪心思想来源于导弹系统题

两个贪心的情况

  • 如果比所有的都大就新开序列
  • 如果比有比他大的就切换掉

本题的实现

dfs枚举每一种情况使用的个数

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

const int N = 55;
int h[N];
int up[N], down[N];
int res;
int n;

//su 和 sd 就是所用的单调上升和单调下降的使用系统数量
void dfs(int u, int su, int sd)
{
    //如果目前使用的系统过多就不要递归了
    if (su + sd >= res) return;
    //枚举完了
    if (u == n)
    {
        //更新答案
        res = min(res, su + sd);
        return;
    }
    
    //这个思路是从导弹系统题目来的贪心思路
    int k = 0;
    while (k < su && up[k] >= h[u]) k ++ ;  //扫描
    if (k < su)
    {
        //更新一下结尾
        int t = up[k];
        up[k] = h[u];
        dfs(u + 1, su, sd);
        up[k] = t;
    }
    else
    {
        //添加新的子序列结尾,就相当于多用系统
        up[k] = h[u];
        dfs(u + 1, su + 1, sd);
    }

    k = 0;
    while (k < sd && down[k] <= h[u]) k ++ ;    //扫面
    if (k < sd)
    {
        //更新结尾
        int t = down[k];
        down[k] = h[u];
        dfs(u + 1, su, sd);
        //还原现场
        down[k] = t;
    }
    else
    {
        //添加新的子序列,就多用系统
        down[k] = h[u];
        dfs(u + 1, su, sd + 1);
    }
}


int main()
{
    while(cin >> n)
    {
        if(n == 0)break;
     
        //初始化res
        res = n;
        for(int i = 0 ; i < n ; i++)
            cin >> h[i];
        
        dfs(0, 0, 0);
        cout<<res<<endl;
    }
    return 0;
}

Div.4 Round849 A Codeforces Checking

题目大意

问你字母有没有在codeforces中出现过

题目理解

这个只是为了让我今天,那个点点绿起来写的。。。打卡的。。。蒙骗自己属于是。。。。

void solve()
{
	string s = "codeforces";

	char c;
	cin >> c;

	if(s.find(c) < 15) cout << "YES" << endl;
	else cout << "NO" << endl;


	return;
}

标签:道题,14,int,res,void,第三十九,cin,max,序列
From: https://www.cnblogs.com/wxzcch/p/17756070.html

相关文章

  • 143-6
    二叉树的结点值各不相同,先序序列和中序序列分别存储在数组A和B中,设计算法构建二叉树使用递归方法。1、使用指针传入数组,方便进行根节点操作。TreeNode*BuildTree(int*A,intAlen,int*B,intBlen)T->lchild=BuildTree(A+1,index,B,index);T->rchild=BuildTree(A+i......
  • 143-5
    非递归求树高current记录每次入队出队操作后队列中结点数,每一个结点出队时--,每一个结点入队时++previous记录每层结点数,出队操作时--当previous==0时,即队列中不存在该层结点且当前队列中是下一层结点。此时,high++。让previous=current,使得previous记录下一层结点数#include<......
  • 狂飙14GB/s!忆恒创源PBlaze7 7940 6.4TB SSD图赏
    国内知名企业级固态存储厂商忆恒创源日前发布了全新一代PCIe5.0企业级NVMeSSD,型号“PBlaze77940”,相比于PCIe4.0产品有了质的飞跃。现在,忆恒创源PBlaze779406.4TBSSD已经来到我们评测室,下面为大家带来图赏。忆恒创源PBlaze系列产品在数据库、虚拟化、云计算、大数据、人......
  • CH9141OTA
    CH9141OTA方式(转载)1、获取版本号(方法一:步骤:①将模块设置工作在从机模式下(已是从机模式就忽略);②使用安卓端CH9141调试APP,对模块进行连接;③连接成功后,获取模块的参数配置;④版本号在“配置参数”->“模块参数”->“版本号”(方法二:步骤:①使用串口与模块相连;②使用AT命令进入AT模式;......
  • CS144-lab1
    Checkpoint1Writeup该lab要根据首字母索引来对收到的字符串进行重组,还原为原始数据(字符串可能乱序到达,可能有重叠)思路是将按顺序并小于可用容量的字符串(可能是部分子串)直接推流到输出流,将失序但在可用容量内的字符串放入本地buffer。考虑到最好用首字符索引对收到的字符......
  • 题解 P3894【[GDOI2014] 传送】
    难倒不难,128MB的空间限制快恶心死我了。我们设\(d_{u_0,u_1}\)表示到节点\((u_0,u_1)\)距离最近的叶子的距离,这个可以很容易换根DP求出。设\(p_{u_0}\)表示树\(u_0\)中距离最近的两个叶子的距离。设\(dis(u_0,u_1,v_0,v_1)\)(\(u_0=v_0\))表示树中两个节点\(u_1\)和......
  • CS144-lab0
    Checkpoint0Writeup该lab要实现一个字节流,兼具写入和读出的能力,并且buffer空间受限。根据要实现的函数和读写功能,内部要存储的成员为std::queue<std::string>buffer_{};用于存储写入的字符串(原本用的std::queue,但由于queue内存不连续,调用peek返回string_view时还要将cha......
  • 复习课14 初识数组
    一.问题导入现需要编写一个程序,程序需要有26个变量,每一个变量都需要存储一个整型数字,所以我们需要将代码写成如下形式:intmain(void){inta=1;intb=2;intc=3;……………intz=26;return0;}我们很快就会发现这样写的弊端:1.代码冗杂重复2.无法使用循......
  • 143-4
    二叉树自下而上,从右到左的层次遍历算法相较于普通的层次遍历,该算法,只是利用栈的特点,将一系列元素反转。层次遍历的每一个结点依次进栈,最后再将依次出栈#include<stdio.h>#include<stdlib.h>#defineMaxSize100typedefstructnode{intdata;structnode*lc......
  • 【做题笔记】CF 1400-1600 构造题
    本人比较菜,所以做的rating很低/kk/kk/kk欢迎各位大佬嘲讽这个蒟蒻/kk/kk/kk/kk$*$表示看了题解才过的(所以你会发现这里的大部分题后面都会有$*$)实时通过率直接贴在后面当不看题解通过率稳定在\(50\%\)以上就弃坑。希望早日弃坑ABBCorBACB*题面中给了两种操作......