大一上第七周学习笔记

这周找回节奏

早睡早起,晨跑,记录每日成果。注意劳逸结合

冲冲冲,加油

 

10.26 周一

 

P1280 尼克的任务

这道题想了3天,没有突破

然后就看了题解了。至少思考过两三天还是没有思路才可以看题解,严格遵守

看了题解焕然大悟

我思考的时候一直卡在一个点,就是当前这个任务的选择会影响后来的选择

也即是有后效性。动态规划要求无后效性,也就是当前的选择只与之前有关,不能影响未来的选择

我就一直卡在这,不知道怎么办

看了题解,逆向思维,倒着搜,就可以避免后效性

太秀了我天

这道题最大的收获就是逆向思维,有些题目反着想就可以突破

卡住时提醒自己反着想,逆向思维

还有一点是不一定是前i个任务,可以是前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 = 1e4 + 10;
vector<int> a[MAXN];
int f[MAXN], n, k;

int main()
{
	scanf("%d%d", &n, &k);
	_for(i, 1, k) 
	{
		int l, t;
		scanf("%d%d", &l, &t);
		a[l].push_back(t);
	}
	
	for(int i = n; i >= 1; i--)
	{
		if(!a[i].size()) f[i] = f[i+1] + 1;
		else REP(j, 0, a[i].size()) f[i] = max(f[i], f[i+a[i][j]]);
	}
	printf("%d\n", f[1]);
	
	return 0;
}

 

线性动规告一段落

开始刷区间dp

 

P1880 [NOI1995]石子合并

区间dp裸题

几个点注意一下

一 f[i][j]表示i到j最大价值,这个问题显然具有最优子结构。然后求f[i][j]的时候要枚举断点,为了保证前面的状态都已经推过,即从小推到大,第一层循环就要枚举区间长度

然后断点k断成f[i][k], f[k+1][j]。f[i][j] = max(f[i][k] + f[k+1][j]) + 合并价值(这里就是i到j的和)  这里注意区间长度从2开始,因为区间长度为1的价值为0(根据题意)

二 环怎么处理 再加一段,更新答案的时候区间为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 = 220;
int f1[MAXN][MAXN], f2[MAXN][MAXN], a[MAXN], s[MAXN], n;

int main()
{
	scanf("%d", &n);
	_for(i, 1, n)
	{
		scanf("%d", &a[i]);
		a[i+n] = a[i];
	}
	_for(i, 1, 2 * n) s[i] = s[i-1] + a[i];
	
	
	int ans1 = 1e9, ans2 = 0;
	_for(len, 2, n)
		_for(i, 1, 2 * n)
		{
			int j = i + len - 1;
			if(j > 2 * n) break;
			f1[i][j] = 1e9;
			_for(k, i, j - 1)
			{
				f2[i][j] = max(f2[i][j], f2[i][k] + f2[k+1][j] + s[j] - s[i-1]);
				f1[i][j] = min(f1[i][j], f1[i][k] + f1[k+1][j] + s[j] - s[i-1]);
			}
			if(len == n) ans2 = max(ans2, f2[i][j]), ans1 = min(ans1, f1[i][j]);
		}
	printf("%d\n%d", ans1, ans2);
	
	return 0;
}

 

P3146 [USACO16OPEN]248 G

这道题和石子合并很像

注意几个点

一 注意i=j时区间就是一个点,这个时候没有断电。所以提前初始化,然后len从2开始

二 区间dp相当于可以枚举所有情况,这里想一下可以得出满足最优子结构(用反证法) 然后这里合并是有条件的,所以很多状态是不合法的,要注意

#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 = 300;
int f[MAXN][MAXN], n;

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &f[i][i]);
	
	int ans = 0;
	_for(len, 2, n)
		_for(i, 1, n)
		{
			int j = i + len - 1;
			if(j > n) break;
			f[i][j] = -1e9;
			_for(k, i, j - 1)
				if(f[i][k] == f[k+1][j])
					f[i][j] = max(f[i][j], f[i][k] + 1);
			ans = max(ans, f[i][j]);
		}
	printf("%d\n", ans);
	
	return 0;
}

 

P1063 能量项链

和前两题差不多

区间dp有点死板??

#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 = 210;
int f[MAXN][MAXN], a[MAXN], n;

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &a[i]), a[n+i] = a[i];
	
	int ans = 0;
	_for(len, 2, n)
		_for(i, 1, 2 * n)
		{
			int j = i + len - 1;
			if(j > 2 * n) break;
			_for(k, i, j - 1)
			{
				int t = j == 2 * n ? 1 : j + 1; 
				f[i][j] = max(f[i][j], f[i][k] + f[k+1][j] + a[i] * a[k+1] * a[t]);
			}
			if(len == n) ans = max(ans, f[i][j]);
		}
	printf("%d\n", ans);
	
	return 0;
}

 

区间dp先到这

这种区间合并的问题都可以用区间dp解

开始树形dp

 

P1352 没有上司的舞会

树形dp入门题

#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 = 6e3 + 10;
vector<int> g[MAXN];
int f[MAXN][2], v[MAXN], in[MAXN], n;

void dfs(int x)
{
	f[x][1] = v[x];
	REP(i, 0, g[x].size())
	{
		int u = g[x][i]; dfs(u);
		f[x][1] += f[u][0];
		f[x][0] += max(f[u][0], f[u][1]);
	}
}

int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &v[i]);
	_for(i, 1, n - 1)
	{
		int l, k; scanf("%d%d", &l, &k);
		g[k].push_back(l); in[l]++;
	}
	
	_for(i, 1, n) 
		if(!in[i])
		{
			dfs(i);
			printf("%d\n", max(f[i][1], f[i][0]));
			break;
		}
			
	return 0;
}

 

一口气刚了5题

今天效率很高啊

上星期很忙写得少了,然后就浑身不舒服

今天时间比较多,重新开始写题还是很爽的

加油,继续干,一点一点稳步前进

 

10.27 周二

 

P2016 战略游戏

这道题昨天看错题了导致没做出来

认真审题。。。。。

注意这是无根树。

有根树边有方向,有父亲和儿子,无根树是双向边(搜索时用fa)没有父亲和儿子这个概念

这时我们随便找个点作根,变成有根树

同时注意树形dp时只考虑父亲和儿子,父亲的父亲不用理

#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 = 2e3;
vector<int> g[MAXN];
int f[MAXN][2], n;

void dfs(int u, int fa)
{
	f[u][1] = 1;
	REP(i, 0, g[u].size())
	{
		int v = g[u][i]; 
		if(v == fa) continue;
		dfs(v, u);
		f[u][1] += min(f[v][1], f[v][0]);
		f[u][0] += f[v][1];
	}
}

int main()
{
	scanf("%d", &n);
	_for(i, 1, n)
	{
		int x, k, t; scanf("%d%d", &x, &k);
		while(k--)
		{
			scanf("%d", &t);
			g[x].push_back(t);
			g[t].push_back(x);
		}
	}
	
	dfs(1, -1);
	printf("%d\n", min(f[1][1], f[1][0]));
			
	return 0;
}

 

开始复习树上背包

P2014 [CTSC1997]选课

树上背包模板题

注意几点

一 这里没有父亲的,全部父亲为0节点,这样森林变成了一棵树,非常方便,同时这个0一定要选,m++就好

二 这其实就是再更新的时候用上分组背包,如果分组背包理解透彻,这个代码很容易理解的。我发现很多题目都可以转化为分组背包的模型,分组背包的应用范围比较广

这里就是把件数看作了费用,每个物品费用为1

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

const int MAXN = 310;
int f[MAXN][MAXN], n, m;
vector<int> g[MAXN];

void dfs(int u)
{
	REP(i, 0, g[u].size())
	{
		int v = g[u][i]; dfs(v);
		for(int j = m; j >= 1; j--)
			_for(k, 0, j - 1)
				f[u][j] = max(f[u][j], f[u][j-k] + f[v][k]);
	}
}

int main()
{
	scanf("%d%d", &n, &m); m++;
	_for(i, 1, n)
	{
		int x; scanf("%d%d", &x, &f[i][1]);
		g[x].push_back(i);
	}
	
	dfs(0);
	printf("%d\n", f[0][m]);
	
	return 0;
}

 

实力恢复了不少,现在可以尝试一些蓝题了

 

P1273 有线电视网

蓝题也就这样哈哈 
一开始想用f[u][j]表示当前钱数为j的时候的最大的观众
但我发现负数不好表示,而且题目也没给出钱总和的范围,数组范围都不知道是多少
这个时候说明这么设有问题
然后我休息一下,回来的时候灵光一闪
不一定要把答案作为值,可以把答案放到维度里面
也就是f[u][j]是表示j个观众时最多赚了多少钱,然后统计答案的时候检查合法性
这是逆向思维,很秀。设状态不一定要那么直接把最后答案设在状态的值上,可以
把答案设为一个维度,最后检查合法性

然后这里要注意初始化的问题。数组默认为0,因为有负数的存在,所以这个0是有意义的
应该先全部设为不合法,也就是负无穷。不过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;

const int MAXN = 3e3 + 10;
struct node { int v, w; };
vector<node> g[MAXN];
int f[MAXN][MAXN], cnt[MAXN], n, m;

void dfs(int u)
{
	REP(i, 0, g[u].size())
	{
		int v = g[u][i].v, w = g[u][i].w;
		dfs(v); cnt[u] += cnt[v]; 
		for(int j = cnt[u]; j >= 1; j--)
			_for(k, 1, j)
				f[u][j] = max(f[u][j], f[u][j-k] + f[v][k] - w);		
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	_for(i, 1, n - m)
	{
		int k, v, w;
		scanf("%d", &k);
		while(k--)
		{
			scanf("%d%d", &v, &w);
			g[i].push_back(node{v, w});
		}
	}
	_for(i, 1, n)
		_for(j, 1, n)
			f[i][j] = -1e9;
	_for(i, n - m + 1, n) 
	{
		int x; scanf("%d", &x);
		f[i][1] = x; cnt[i] = 1;
	}
	
    dfs(1);
	for(int i = m; i >= 0; i--)
		if(f[1][i] >= 0)
		{
			printf("%d\n", i);
			break;
		}
	
	return 0;
}

 

10.28 周三

最近有点飘

谦逊低调少炫耀装逼

今天一直在刚重建道路的,有几个点没想透,继续刚

明天应该能刚掉

然后就开始打洛谷比赛和刷cf,一直持续到寒假

 

10.29 周四

神tm还是不会写

太菜了,明天继续刚。

 

先放放,看看怎么用codefoces刷题打比赛

codeforces游玩攻略

这周六下午打一波洛谷月赛

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值