大一上第五周学习笔记

10.12 周一

 

P3373 【模板】线段树 2

这道题真的一波三折

这题干了好久,大概想了三四天,每天一两个小时刚这道题,终于tm独立想出来了

这题要写一个支持区间每个数乘法的线段树

第一个坎是怎么弄标记,这里我卡了挺久

想用一个标记,发现不行。那就用两个标记,加和乘

但是两个标记怎么共存,什么顺序,我卡了

计算机导论课嫌无聊自己回来想这道题,突然醒悟先乘后加,先加后乘很麻烦

然后交上去发现30分

又懵逼了

然后又过了几天,突然醒悟一个条件写错了,乘法标记并不是大于1才下放,因为可能乘以0

所以我还是忽略了极端情况,极大或0

最后还是过了

坚持不看题解,独立思考,坚持下去!!

#include<bits/stdc++.h>
#define l(k) (k << 1)
#define r(k) ((k << 1) + 1)
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

typedef long long ll;
const int MAXN = 1e5 + 10;
int MOD;
struct node
{
	ll f1, f2, w;
	int l, r;
	int len() { return r - l + 1; }
	int m() { return (l + r) >> 1; }
}t[MAXN << 2];

//1 jia 2 cheng

void lazy(int k, ll p, int q) 
{ 
	if(q == 1) 
	{
		t[k].f1 = (t[k].f1 + p) % MOD;
		t[k].w = (t[k].w + t[k].len() * p) % MOD;
	}
	else
	{
		t[k].f1 = t[k].f1 * p % MOD;
		t[k].f2 = t[k].f2 * p % MOD;
		t[k].w = t[k].w * p % MOD;
	}
}

void up(int k) { t[k].w = (t[l(k)].w + t[r(k)].w) % MOD; }

void down(int k, int q)
{
	if(q == 1)
	{
		lazy(l(k), t[k].f1, q);
		lazy(r(k), t[k].f1, q);
		t[k].f1 = 0;
	}
	else
	{
		lazy(l(k), t[k].f2, q);
		lazy(r(k), t[k].f2, q);
		t[k].f2 = 1;
	}
}

void build(int k, int l, int r)
{
	t[k].l = l; t[k].r = r; t[k].f1 = 0; t[k].f2 = 1;
	if(l == r)
	{
		scanf("%lld", &t[k].w);
		return;
	}
	int m = t[k].m();
	build(l(k), l, m);
	build(r(k), m + 1, r);
	up(k);
}

void change(int k, int L, int R, ll p, int q)
{
	if(L <= t[k].l && t[k].r <= R)
	{
		lazy(k, p, q);
		return;
	}
	if(t[k].f2 != 1) down(k, 2); if(t[k].f1) down(k, 1);
	int m = t[k].m();
	if(L <= m) change(l(k), L, R, p, q);
	if(R > m) change(r(k), L, R, p, q);
	up(k);
}

ll sum(int k, int L, int R)
{
	if(L <= t[k].l && t[k].r <= R) return t[k].w % MOD;
	if(t[k].f2 != 1) down(k, 2); if(t[k].f1) down(k, 1);
	ll res = 0;
	int m = t[k].m();
	if(L <= m) res = (res + sum(l(k), L, R)) % MOD;
	if(R > m) res = (res + sum(r(k), L, R)) % MOD;
	return res;
}

int main()
{
	int n, m, q, x, y, k;
	scanf("%d%d%d", &n, &m, &MOD);
	build(1, 1, n);
	
	while(m--)
	{
		scanf("%d%d%d", &q, &x, &y);
		if(q <= 2)
		{
			if(q == 1) q = 2; else q = 1;
			scanf("%d", &k);
			change(1, x, y, k, q);
		}
		else printf("%lld\n", sum(1, x, y));
	}

	return 0;
}

 

发现寒训内容非常大,还是要多提前复习自学

现在开始复习动态规划

先从01背包开始

背包9讲写得太好了

 

01背包模板

这个方法有亮点

一 空间优化 发现方程每次只与上一行有关,所以可以把二维变成一维,而且要逆推

奇妙的是顺推就变成完全背包了

二 初始化问题 如果要求完全装满,可以设置f[0] = 0,其他负无穷,表示不合法

三 注意数组空间,f是总重量不是物品个数

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1e3 + 10;
const int MAXM = 1e5 + 10;
int f[MAXM], c[MAXN], w[MAXN], n, v;

int main()
{
	scanf("%d%d", &n, &v);
	_for(i, 1, n) scanf("%d%d", &w[i], &c[i]);
	_for(i, 1, n)
		for(int j = v; j >= w[i]; j--)
			f[j] = max(f[j], f[j-w[i]] + c[i]);
	printf("%d\n", f[v]);
	return 0;
}

 

RQNJOJ 72

这个是01背包的一个转化,即可以推出所有可能的重量组合

1表示存在,0表示不存在

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1e2 + 10;
const int MAXM = 1e4 + 10;
int m[MAXN], f[MAXM], n, v;

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &m[i]), v += m[i];
	f[0] = 1;
	_for(i, 1, n)
		for(int j = v; j >= m[i]; j--)
			f[j] |= f[j-m[i]];
			
	int t = v / 2;
	while(!f[t]) t--;
	printf("%d\n", v - t - t);
	
	return 0;
}

 

完全背包

前面已经讲了,第二层循环顺序就是完全

背包九讲中谈到一个物品化成多个物品转化成01背包额思路

其中谈到可以用二进制拆,而不是1234这样

很秀,可能以后其他题目可以用到

 

RQNOJ 162

完全背包裸题

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 5e3 + 10;
const int MAXM = 1e4 + 10;
int f[MAXM], w[MAXN], v[MAXN], n, m;

int main()
{
	scanf("%d%d", &n, &m);
	_for(i, 1, n) scanf("%d%d", &w[i], &v[i]);
	_for(i, 1, m) f[i] = -1e9;
	
	_for(i, 1, n)
		_for(j, w[i], m)
			f[j] = max(f[j], f[j-w[i]] + v[i]);
	printf("%d\n", f[m]);

	return 0;
}

 

大致规划一下,今天学习前三种背包

明天学完剩下的背包

后天刷背包杂题

 

多重背包

多重背包化成01背包就完事,用二进制拆

RQNOJ 98

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXM = 500 + 10;
struct node{ int w, v; };
vector<node> g;
int f[MAXM], n, m;

int main()
{
	scanf("%d%d", &n, &m);
	_for(i, 1, n)
	{
		int k, w, v;
		scanf("%d%d%d", &k, &w, &v);
		
		int t = 1;
		while(k > t * 2 - 1)
		{
			g.push_back(node{w * t, v * t});
			t <<= 1;
		}
		t = k - t + 1;
		g.push_back(node{w * t, v * t});
	}
	
	REP(i, 0, g.size())
	{
		int w = g[i].w, v = g[i].v;
		for(int j = m; j >= w; j--)
			f[j] = max(f[j], f[j-w] + v);
	}
	printf("%d\n", f[m]);
	
	return 0;
}

 

10.13 周二

 

二维费用背包

在原来的基础上多加一维即可,这是动态规划题目多增加条件后的常用方法

RQNOJ 57

这道题要充分利用多加维度的思想

我这里多加了两维,但真正的实现的时候有挺多细节要处理好

 

一 初始化 把最开始f[0][0][0]为0,其他全部正无穷,包括f[0][0][1]之类的,下标从0开始遍历

同时注意这样有无穷的处理之后,费用就是恰好这个费用的状态。答案可以边做边统计

 

二 自己现在纸上把状态和转移方程搞清楚,写程序才清晰明了

状态是四维,要四层循环,循环时为了只放一次,要逆序。空间上可以省去一维,因为这一维只与上一行有关

 

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 110;
int f[MAXN][MAXN][MAXN], w1[MAXN], w2[MAXN], t[MAXN];
int n, m1, m2;

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d%d%d", &w1[i], &w2[i], &t[i]);
	scanf("%d%d", &m1, &m2);
	
	_for(i, 0, m1)
		_for(j, 0, m2)
			_for(k, 0, n)
				f[i][j][k] = 1e9;
	f[0][0][0] = 0;  
	
	int ans = 1e9, ma = 0;
	_for(i, 1, n)
		for(int j = m1; j >= w1[i]; j--)
			for(int k = m2; k >= w2[i]; k--)
				for(int p = i; p >= 1; p--)
				{
					int temp = f[j-w1[i]][k-w2[i]][p-1] + t[i];
					if(f[j][k][p] > temp)
					{
						f[j][k][p] = temp;
						if(p > ma) ma = p, ans = temp;
						else if(p == ma && temp < ans) ans = temp;
					}
				}
	printf("%d\n", ans);
	
	return 0;
}

上面是一种复杂度n四次方的做法,因为n<=100所以可以做,但n大一点就tle了

还有一种n的三次方做法,挺秀的,这种思路要学习

这里要求最大价值时的最小时间

那就开两个数组,一个维护价值,一个维护时间,同步更新

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 110;
int f1[MAXN][MAXN], f2[MAXN][MAXN], w1[MAXN], w2[MAXN], t[MAXN];
int n, m1, m2;

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d%d%d", &w1[i], &w2[i], &t[i]);
	scanf("%d%d", &m1, &m2);
	
	int ans = 1e9, ma = 0;
	_for(i, 1, n)
		for(int j = m1; j >= w1[i]; j--)
			for(int k = m2; k >= w2[i]; k--)
			{
				int t1 = f1[j-w1[i]][k-w2[i]] + 1;
				int t2 = f2[j-w1[i]][k-w2[i]] + t[i];
				if(t1 > f1[j][k] || (t1 == f1[j][k] && t2 < f2[j][k]))
				{
					f1[j][k] = t1, f2[j][k] = t2;
					if(t1 > ma || (t1 == ma && t2 < ans)) ma = t1, ans = t2; 
				}
			}
	printf("%d\n", ans);
				
	return 0;
}

 

恪守一件事

把能控制的事情做到最好

不能控制的事情不理它

 

分组背包

分组背包的核心问题是如何保持一组的物品最多放一个
答案是把费用的循环放在枚举物品循环的外面
为什么把费用的循环放在枚举物品外面就能保证同一组只放一次呢

如果先物品后费用,那么在更新的过程中,f[j-w[i]]就包含了i之前的物品,也就是既放了之前的物品,又放了现在的物品
即可以放多个物品
如果先费用后物品,那么在更新的过程中,f[j-w[i]]就不包含同组的物品,因为是逆序的

有点抽象,我也想了蛮久

P1757 通天之分组背包

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1e3 + 10;
int f[MAXN], w[MAXN], v[MAXN], k[MAXN], n, m, cnt;

int main()
{
	scanf("%d%d", &m, &n);
	_for(i, 1, n)
	{
		scanf("%d%d%d", &w[i], &v[i], &k[i]);
		cnt = max(cnt, k[i]);
	}
	
	_for(p, 1, cnt)
		for(int j = m; j >= 0; j--)
			_for(i, 1, n)
				if(k[i] == p && j >= w[i])
					f[j] = max(f[j], f[j-w[i]] + v[i]);
	printf("%d\n", f[m]);
	
	return 0;
}

 

有依赖的背包问题

 

P1064 金明的预算方案

前面题都独立做出,现在做这道题就很简单了

看似有依赖麻烦,其实利用拆分物品的思想就可以转化为分组背包了

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 100;
const int MAXM = 32000 + 10;
int f[MAXM], w[MAXN], v[MAXN], k[MAXN], n, m;

void update(int j, int w, int v)
{
	if(j >= w) f[j] = max(f[j], f[j-w] + v);
}

int main()
{
	scanf("%d%d", &m, &n);
	_for(i, 1, n)
	{
		scanf("%d%d%d", &w[i], &v[i], &k[i]);
		v[i] *= w[i];
	}
	
	_for(p, 1, n)
		if(k[p] == 0)
			for(int j = m; j >= 0; j--)
			{
				update(j, w[p], v[p]);
				
				int cnt = 0, t[2] = {0};
				_for(i, 1, n)
					if(k[i] == p)
						t[cnt++] = i;
				
				if(cnt == 1) update(j, w[p] + w[t[0]], v[p] + v[t[0]]);
				if(cnt == 2)
				{
					update(j, w[p] + w[t[0]], v[p] + v[t[0]]);
					update(j, w[p] + w[t[1]], v[p] + v[t[1]]);
					update(j, w[p] + w[t[0]] + w[t[1]], v[p] + v[t[0]] + v[t[1]]);
				}
			}
	printf("%d\n", f[m]);

	return 0;
}

 

今天到这,时间花的比我想的要久

不过没事,认真思考,独立写题,稳扎稳打就好,不要使劲赶进度

加油

 

10.14 周三

 

poj 2392

多重背包
二进制拆法要清晰
注意这里还要再排序处理一下 

#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
const int MAXN = 500;
const int MAXM = 4e4 + 10;
struct node
{
	int h, k, m;
	bool operator < (const node& rhs) const
	{
		return m < rhs.m;
	}
}a[MAXN];
int f[MAXM], n, mm;
 
void update(int i, int h)
{
	for(int j = a[i].m; j >= h; j--)
		f[j] |= f[j-h];
}
 
int main()
{
	scanf("%d", &n);
	_for(i, 1, n) 
	{
		scanf("%d%d%d", &a[i].h, &a[i].m, &a[i].k);
		mm = max(mm, a[i].m);
	}
	sort(a + 1, a + n + 1);
	
	f[0] = 1;
	_for(i, 1, n)
	{
		int t = 1;
		while(a[i].k > t * 2 - 1)  // t * 2 - 1表示目前所有的和 
		{
			update(i, a[i].h * t);
			t <<= 1;
		}
		t = a[i].k - (t - 1);   // t - 1示目前所有的和 
		update(i, a[i].h * t);
	}
	
	while(!f[mm]) mm--;
	printf("%d\n", mm);
	
	return 0;
}

P1504 积木城堡

01背包推出所有可能即可

昨晚晚睡了,现在状态迷离

 

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
const int MAXN = 110;
const int MAXM = 1e4 + 10;
int f[MAXN][MAXM], w[MAXN], ans = 1e9, n;
 
int main()
{
	scanf("%d", &n);
	_for(k, 1, n)
	{
		int cnt = 0, x, m = 0;
		while(1)
		{
			scanf("%d", &x);
			if(x == -1) break;
			w[++cnt] = x; m += x;
		}
		
		f[k][0] = 1;
		ans = min(ans, m);
		_for(i, 1, cnt)
			for(int j = m; j >= w[i]; j--)
				f[k][j] |= f[k][j-w[i]];
	}
	
	while(1)
	{
		int flag = 1;
		_for(i, 1, n)
			if(!f[i][ans])
			{
				flag = 0;
				break;
			}
		if(flag) 
		{
			printf("%d\n", ans);
			break;
		}
		ans--;
	}
	
	return 0;
}

 

最长不下降子序列

现在开始复习简单的dp

一定要独立思考,自己想清楚

n方做法很显然,复习了nlogn的做法

这个做法的亮点是d数组

表示长度为i的最长不下降子序列的最小末尾元素

很骚

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1e3 + 10;
int a[MAXN], d[MAXN], n, len;

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &a[i]);
	
	d[1] = a[1]; len = 1;
	_for(i, 2, n)
	{
		if(a[i] >= d[len]) d[++len] = a[i];
		else d[upper_bound(d + 1, d + len + 1, a[i]) - d] = a[i];
	}
	printf("%d\n", len);
	
	return 0;	
} 

 

10.15 周四

 

P1439 【模板】最长公共子序列

本来想练一下模板的

没想到要用nlogn的做法

这题给的是1到n的全排列,很特殊

方法很秀,强行有序

就是把第一个序列都标号1到n

然后第二个序列就改成对应的id

然后第二个序列额最长不下降子序列就是答案

太秀了

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1e5 + 10;
int a[MAXN], b[MAXN], id[MAXN], n;
int d[MAXN], len;

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &a[i]), id[a[i]] = i;
	_for(i, 1, n) 
	{
		int x; scanf("%d", &x);
		b[i] = id[x];
	}
	
	d[1] = b[1]; len = 1;
	_for(i, 2, n)
	{
		if(b[i] >= d[len]) d[++len] = b[i];
		else d[upper_bound(d + 1, d + len + 1, b[i]) - d] = b[i];
	}
	printf("%d\n", len);
	
	return 0;	
} 

 

学会从数据范围反推时间复杂度

ACM-由数据范围反推算法复杂度以及算法内容

 

10.17 周六

 

昨天很疲惫就没写题了。可以接受,学会劳逸结合。

P1802 5倍经验日

01背包变形

可以看作分组背包,每一组有两种策略

或者01背包,有两种更新方法

注意竟然有可能费用为0,这里坑了我挺久

看数据范围时不仅仅看最大来看时间复杂度

还要看最小的,比如可能为0,这里有坑

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)  
using namespace std;

typedef long long ll;
const int MAXM = 1e3 + 10;
const int MAXN = 1e3 + 10;
int f[MAXM], w[MAXN], v1[MAXN], v2[MAXN], n, m;

int main()
{
	scanf("%d%d", &n, &m);
	_for(i, 1, n) scanf("%d%d%d", &v1[i], &v2[i], &w[i]);
	_for(i, 1, n)
		for(int j = m; j >= 0; j--)
		{
			if(w[i] == 0) f[j] += v2[i];
			else
			{
				f[j] += v1[i];
				if(j >= w[i]) f[j] = max(f[j], f[j-w[i]] + v2[i]);
			}
		}
	printf("%lld\n", (ll)f[m] * 5);
	return 0;
}

 

P2196 挖地雷

看到数据范围直接暴搜

这里的记录路径需要学

不一定逆推,顺推也可以,看情况

每次找最优的记录路径

注意每次搜的时候初始化数组,这些细节要注意

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)  
using namespace std;

const int MAXN = 25;
int v[MAXN], vis[MAXN], Map[MAXN][MAXN];
int r[MAXN], k[MAXN], flag, ans, st, n;

int dfs(int x)
{
	vis[x] = 1;
	int ma = 0;
	_for(i, 1, n)
		if(Map[x][i] && !vis[i])
		{
			int t = dfs(i);
			if(ma < t) ma = t, r[x] = i;
		}
	vis[x] = 0;
	return v[x] + ma;
}

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &v[i]);
	_for(i, 1, n)
		_for(j, i + 1, n)
			scanf("%d", &Map[i][j]); 
			
	_for(i, 1, n) 
	{
		memset(r, 0, sizeof(r));
		int t = dfs(i);
		if(ans < t) 
		{
			ans = t, st = i;
			_for(i, 1, n) k[i] = r[i];
		}
	}
	for(int i = st; i; i = k[i]) printf("%d ", i);
	printf("\n%d\n", ans);
	
	return 0;
}

 

10.18 周日

 

编程才是我真正热爱的东西

真正的享受在其中

加油

 

P2196 挖地雷

这道题用搜索做出,后来开始想动规的做法

一开始想到f[i][j]表示前i个点,以j为终点的状态

但是发现如果用j为终点这样更新有问题

然后就懵逼了

因为这道题已经做出,就懒得再想了,所以看了别人的动规写法

发现就是我原来想的这样,此外i这一维没有用,可以省掉(写出方程可以发现)

为什么可行呢

后来我发现是因为这个题目的奇葩设定

这里的边一定是单向边

而且一定是从标号小的到标号大的

这种图我还是第一次见

就是这种标号的单向性使得这种做法可行

不过我看好像评论区没有人说到这点

这个做法完全是得益于这个奇葩数据,正常一点的数据这个做法是不行的

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)  
using namespace std;

const int MAXN = 25;
int v[MAXN], Map[MAXN][MAXN], r[MAXN], f[MAXN], n;

void print(int x)
{
	if(!x) return;
	print(r[x]);
	printf("%d ", x); 
}

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &v[i]);
	_for(i, 1, n)
		_for(j, i + 1, n)
			scanf("%d", &Map[i][j]);
			
	_for(i, 1, n)
	{
		f[i] = v[i];
		_for(j, 1, i - 1)
			if(Map[j][i] && f[i] < f[j] + v[i])
			{
				f[i] = f[j] + v[i];
				r[i] = j; 
			}
	}

	int ans = 0, end;
	_for(i, 1, n)
		if(ans < f[i])
		{
			ans = f[i];
			end = i;
		}
	print(end);
	printf("\n%d\n", ans);
	
	return 0;
}

 

P1434 [SHOI2002]滑雪

代码写完后干两件事 一从头到尾检查一遍,不要犯低级错误 二试一下极限小和极限大的数据

这道题我写了个记忆化搜索过了

还有另外一种动态规划解法

为了保持无后效性,将高度排序,然后就用类似最长不下降子序列n方的解法做就可以

动规中要考虑无后效性

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)  
using namespace std;

const int MAXN = 110;
int a[MAXN][MAXN], f[MAXN][MAXN], n, m, ans;
int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};

int dfs(int x, int y)
{
	int t = 0;
	REP(i, 0, 4)
	{
		int xx = x + dir[i][0], yy = y + dir[i][1];
		if(xx < 1 || yy < 1 || xx > n || yy > m || a[xx][yy] >= a[x][y]) continue;
		if(f[xx][yy]) t = max(t, f[xx][yy]);
		else t = max(t, dfs(xx, yy));
	}
	ans = max(ans, 1 + t);
	return f[x][y] = 1 + t;
}

int main()
{
	scanf("%d%d", &n, &m);
	_for(i, 1, n)
		_for(j, 1, m)
			scanf("%d", &a[i][j]);
	
	_for(i, 1, n)
		_for(j, 1, m)
			if(!f[i][j])
				dfs(i, j);
	printf("%d\n", ans);
	
	return 0;
}

 

训练不要着急,不要忙着赶进度,衡量训练效果的不是题目数量,而是你究竟学到了什么。耐心一道题一道题踏踏实实独立做出做好总结,速度慢一点也没关系

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值