做题记录

P1280 尼克的任务

题意:

题目传送门

思路:

很明显这一道dp题目,考虑dp[i]的含义,容易想到表示1~n时间内摸鱼时间的最大值。
接下来考虑转移方程。考虑在时间为i时到达岗位开始工作,即在岗时间为i~n。此时如果有任务,即可考虑从任务结束时间转移过来。不难看出这种方式有一个特点,即逆序循环到第i位时,一定处于任务开始或摸鱼状态,因为任务时往后计算的,不对前面有影响。所以在转移时不用担心转移的位置处于任务中。

转移方程:

如果当前位置没有任务,那么dp[i]=dp[i+1]+1
如果当前位置有任务,那么dp[i]=max(dp[i],dp[i+v[i][j]]) (v[i][j]表示第i个数的任务的结束位置)

代码

点击查看代码
#include <bits/stdc++.h> 
using namespace std;
const int maxn = 1e4+5;
int n, k;
vector<int>v[maxn];
int dp[maxn];

int main(){
	scanf("%d%d", &n, &k);
	for(int i=1; i<=k; i++){
		int p, t;
		scanf("%d%d", &p, &t);
		v[p].push_back(t);
	}
	int pos=1; 
	for(int i=n; i>=1; i--){
		if(v[i].size() == 0) dp[i] = dp[i+1] + 1;
		else{
			for(int j=0; j<v[i].size(); ++j){
				dp[i] = max(dp[i], dp[i+v[i][j]]);
			}
		}       
	}
	printf("%d", dp[1]);
}

P4310 绝世好题

题意

给定长度为nn1e5)的整数数列aai109),求a的最长子序列长度,满足子序列相邻元素按位与结果均不为0

思路

首先想到的是O(n2)暴力算法:dp[i]表示以a_i为结尾的序列的最长长度,据题意则可以得到状态转移方程对于in,j<i,则若 ai&aj,dp[i]=max(dp[i],dp[j]+1),即本身(dp[i])和以前一位数(第j位)为结尾的序列的最大值(dp[j])加1。最后的答案即为f[i]的最大值,因为长度最长的序列的结尾不一定是最后一位:)。
然后您喜提 90 分的高分:)。

然后我们就必须优化一下:(。
接下来便是了O(n) 的算法。之前是遍历数组,比较每一个数字是否符合条件。然后我们其实可以在得到每一个数字时判断它第j位(二进制位)是否为1,因为只有有一位都有1的两个数才能相邻。所以可以更改一下dp[i][j]的含义,即为表示最后一个数字第j位为1的最长子序列长度。

转移方程

若第i个数字第j位为1, dp[i][j]=max(dp[i1][k])+1, 其中k为数字i中为1的位。
若第i个数字第j位不为1,dp[i][j]=dp[i1][j]

代码

点击查看代码
#include <bits/stdc++.h> 
using namespace std;
const int maxn = 32;
int f[maxn];

int main(){
	int n, mx, ans=mx=0;
	scanf("%d", &n);
//	for(int i=1; i<=n; i++) scanf("%d", &a[i]);
//	for(int i=1; i<=n; i++) f[i]=1;
//	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); 
//		}
//		ans = max(ans, f[i]);
//	}
	for(int i=1; i<=n; i++){
		int x;
		scanf("%d", &x);
		for(int j=0; j<=31; j++) if((1<<j) & x) mx = max(mx, f[j]+1);
		for(int j=0; j<=31; j++) if((1<<j) & x) f[j] = max(mx, f[j]);
		ans = max(mx, ans);
	}
	printf("%d\n", ans);
}

CF1842C Tenzing and Balls

题意

给定n个数字,每次可以选择两个相等的数字,将这两个数字及之间的所有数字删除,求最大删除数字数量。

思路

考虑dp。推理可知如果因两个数字i而删除了n~m位,接下来有出现数字i,此时无视中间的i,就可以删除更大的范围。考虑dp[i][j]含义,即为前i个数字最大删除数字数量,j表示是否删当前数字(0表示不删,1表示删)。为了知道前面是否有与第i位相等的数字和位置,增加lst数组,lst[i]即表示数字大小为i的上一个数。

转移方程

dp[i][0]=max(dp[i1][0],dp[i1][1]);
dp[i][1]=max(dp[lst[a[i]][0]+(ilst[a[i]]+1),dp[lst[a[i]]][1]+(ilst[a[i]]))

代码

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn], lst[maxn], dp[maxn][2];

void solve(){
	int n;
	scanf("%d", &n);
	for(int i=1; i<=n ;i++) scanf("%d", &a[i]);
	for(int i=1; i<=n; i++) lst[i] = 0;
	for(int i=1; i<=n; i++){
		dp[i][0] = max(dp[i-1][0], dp[i-1][1]);
		if(!lst[a[i]]) dp[i][1] = 0;
		else dp[i][1] = max(dp[lst[a[i]]][0]+i-lst[a[i]]+1, dp[lst[a[i]]][1]+i-lst[a[i]]);
		lst[a[i]] = i;
	} 
	printf("%d\n", max(dp[n][0], dp[n][1]));
}

int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		solve();
	}
}

CF1798E Multitest Generator

题意

定义test为第一个数字为x的长度为x+1的序列
定义multiset为第一个数字为x,随后由x个test拼接起来的序列
每次修改可以修改某个元素的值。
求对于 1<=i<=n-1,序列a从i开始的后缀变成multiset需要几次修改。
每个询问的修改独立

思路

不难发现答案最大值为2,即将第一个数字修改为1,第二个数字修改为len-2。答案为0时,原本就是x个multitest。答案为1时,第i位后的数已经为完整的多个test,但是test的数量不等于a[i]。考虑逆序循环,dp[i][j]为进行j次修改后,第i位后的test数量。

转移方程

dp[i][1]=max(num+1,dp[i+a[i]+1][1]+1):a[i],a[i];
dp[i][0]=dp[i+a[i]+1][0]+1(dp[i+a[i]+1][0]!=1)(num为不修改test数量)

代码

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
int dp[maxn][2], a[maxn];

void solve(){
	int n;
	scanf("%d", &n);
	for(int i=1; i<=n; i++) scanf("%d", &a[i]), dp[i][0] = dp[i][1] = -1;
	dp[n+1][0] = 0, dp[n+1][1] = 0;
	int mx = 0;
	for(int i=n; i>=1; i--){
		if (i + a[i] + 1 <= n + 1) dp[i][1] = dp[i + a[i] + 1][1] + 1;
			dp[i][1] = max(dp[i][1], mx + 1);	
			if (i + a[i] + 1 > n + 1 || dp[i + a[i] + 1][0] == -1) dp[i][0] = -1;
			else dp[i][0] = dp[i + a[i] + 1][0] + 1;
			mx = max(mx, dp[i][0]);
	}
	for (int i = 1; i <= n - 1; i++) {
		int ans = 2;
		if (dp[i + 1][0] == a[i]) ans = 0;	
		else if (dp[i + 1][0] != -1) ans = 1;
		else if (a[i] <= dp[i + 1][1]) ans = 1;
		else ans = 2;
		cout << ans << " ";
	}
	cout << endl;
} 

int main(){
	int x;
	scanf("%d", &x);
	for(int i=1;i<=x;i++) solve(); 
}

CF1809F Traveling in Berland

题意

有n个城市围成一圈,第i个能去第i+1个城市,第n个能去第一个城市, 每个城市去下一个城市需要a_i升油。
车的油箱最多能存k升油
每个城市有一个加油站,可以花b[i]块钱加一升油

思路

观察到油价只有1或2,所以碰到1就加满油,碰到2就不加油或加能到下一个点的油。

P1197 [JSOI2008] 星球大战

posted @   niuerdao  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示