做题小结 dp训练3

第一个

这道题 主要思考到一个不可以连续两步 以及最大往左移动5位 就像背包一样
所以我们开个二维的dp数组表示

	for (int j = 1; j <=z ; j++) {
			if (i + j *2<= k + 1 &&i-1>=1) {
				dp[i][j] = max(dp[i][j - 1] + a[i - 1] + a[i], max(dp[i][j],dp[i-1][j]+a[i]));
			}
		}

注意到往左j步 产生回来的话就是j*2 然后这个就是一个边界条件判断
别的就没啥了 挺好的一道题目


int n;
int k;
int z;
int a[range];
int dp[range][30];
int sum[range];
void init()
{
	for(int i=1;i<=n+5;i++){
		for(int j=0;j<=z+5;j++)
		{ a[i]=0;
			dp[i][j]=0;
		}
	}
}
void solve() {
	cin >> n >> k >> z;
	for (int i = 1; i <= n; i++)cin >> a[i];
	for(int i=1;i<=n;i++){
		sum[i]=sum[i-1]+a[i];
	}
//	cout<<sum[35]<<endl;
//    dp[1][0]=a[1];
	for (int i = 1; i <= min(n, k + 1); i++) {
		if (i <= k + 1) {
			dp[i][0] = max(dp[i - 1][0] + a[i], dp[i][0]);
		} else break;
//		for(int j=1;j<=z;j++)
//		{
//			dp[i][j]=max(dp[i-1][j]+a[i],dp[i];
//		}
		//起始位置不算步数
		for (int j = 1; j <=z ; j++) {
			if (i + j *2<= k + 1 &&i-1>=1) {
				//	cout << i << " " << j << " " << i + j - 1 + 2 << endl;
				dp[i][j] = max(dp[i][j - 1] + a[i - 1] + a[i], max(dp[i][j],dp[i-1][j]+a[i]));
			}
			//没有左移两次 有也没事
		}
		for(int j=1;j<=z;j++)
		{
			if(i+j*2<=k+1&&i+1<=n)
			{
				dp[i][j]=max(dp[i][j-1]+a[i+1]+a[i],max(dp[i][j],dp[i-1][j]+a[i]));
			}
		}
	}
	int ans = 0;
	for (int i = 1; i <= min(n, k + 1); i++) {
		for (int j = 0; j <= z; j++)
			if (i + j == k + 1 && j == 0) {
				ans = max(ans, dp[i][j]);
			} else if (j > 0 && j * 2 + i == k + 1) {
				ans = max(ans, dp[i][j]);
			}
	}init();
	cout << ans << endl;
}

题目-字符串蓝题(现在是黄了)

这个题有点狗屎 要求挺多的 又要连着头尾 又要最后一样

不过我没想到最后一样这个其实不就是只输出dp[i][i]就行了

然后头尾其实就是二维表示下即可 dp[i][j]表示i开头j结尾的
然后就是一个状态转移方程

提取这个字符串的头尾
dp[i][j]=max(dp[i][j],dp[i][newt]+len)即可 这个j也是新的字符串的尾巴

然后注意dp[newt][neww]=len 记得赋值下就可以了

for(int i=1;i<=n;i++)
	{
		cin>>x;
	  int g=x[0]-'a'+1;
		int gg=x[x.size()-1]-'a'+1;
		int w=x.size();
		for(int j=1;j<=26;j++)
		{
			if(dp[j][g])
			dp[j][gg]=max(dp[j][gg],dp[j][g]+w);
		}
		dp[g][gg]=max(dp[g][gg],w);	
	}
	for(int i=1;i<=26;i++)
	{
		ans=max(ans,dp[i][i]);
	}

这个题 一开始题目读错了 后面才知道 翻转就行

我还以为是这样的操作 只能对位反转 给我思考了半天

然后其实就没什么了 四个情况 一一对应就好了 这边不列举了

int cost[range];
int a[range];
string s[range];
int dp[range][5];
string temp[range];
void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> cost[i];
	for (int i = 1; i <= n; i++) {
		cin >> s[i];
		temp[i] = s[i];
		reverse(temp[i].begin(), temp[i].end());
	}
	memset(dp, 0x3f, sizeof dp);
	int ini = dp[0][0];
	dp[1][0] = 0;
	dp[1][1] = cost[1];
	for (int i = 2; i <= n; i++) {
		if (s[i] >= s[i - 1]) {
			dp[i][0] = min(dp[i - 1][0], dp[i][0]);
		} if (temp[i - 1] <= s[i]) {
			dp[i][0] = min(dp[i - 1][1], dp[i][0]);
		}  if (temp[i] >= s[i - 1]) {
			dp[i][1] = min(dp[i - 1][0] + cost[i], dp[i][1]);
		}  	if (temp[i - 1] <= temp[i]) {
			dp[i][1] = min(dp[i - 1][1] + cost[i], dp[i][1]);
		} 
	}
	int ans = min(dp[n][1], dp[n][0]);
	if (ans == ini)cout << -1 << endl;
	else
		cout << min(dp[n][1], dp[n][0]) << endl;
}

树形

不错的一道好题
我写了个dfs。。。直接t飞了 我知道会t的。。。

后面思考正解 考虑dp二维表示可行不可行

反正最终答案一定是n对吧
我老是想说一件事情 就是写dp题要有一种大局观 就是上帝视角一样
有一种不拘泥小节的思想 看事情看的很远的视野

这个题就体现的很好

就像背包一样
我们开一层循环1-n 第二层表示 走到i的方式当然要取min k

其实就是个背包这个题。。。。

然后可行的就是j>=d的

状态转移
j>=d:
对于可行可以由可行与不可行转移过来
j<d
不可行呢 不可行不是说我不能从可行转移过来 那如果我之前选了d 此次我选的挺小的 就必须保存答案呀


  for(int i=1;i<=n;i++)
	 {
		 for(int j=1;j<=min(k,i);j++)
		 {
			 if(j>=d)
			 {
			dp[i][1]+=dp[i-j][0]+dp[i-j][1];
				 dp[i][1]%=mod;
			 }
			 else {
			dp[i][1]+=dp[i-j][1];
				 dp[i][1]%=mod;
			dp[i][0]+=dp[i-j][0];	 
				 dp[i][0]%=mod;
			 }
		 }
	 }

好题

	for (int i = 1; i <= 1e3 + 10; i++) {
		for (int j = 1; j <= i; j++) {
			dp[i + i / j] = min(dp[i + i / j], dp[i] + 1);
		}
	}

你可以最多进行 k 次如下的操作:选择两个正整数i,x,使 ai 变成 ai+ai/x

这一步很帅 观察到n只有1000
考虑n方dp 当然你问我n大了怎么办
我也不会。。。这个dp也是很有技巧的 非常的帅
如果n大了其实你会发现到很多的j都是无用的 我想 优化的话应该要用到整除分块的思想 具体我就不知道怎么了 毕竟整除分块都是蓝模板了
跑最短路做不了的 边都建不了
然后dp写完之后 最主要还是要发现k是诈骗 实际上1e3的数据撑不了几十次 所以k多了就是浪费 所以我们太大的k直接输出就行

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define debug cout<<endl<<"----------"<<endl;
using namespace std;
const int range = 1e6 + 10;
int n;
int b[range];
int cost[range];
int dp[5000];
int k;
int ans[range];
void solve() {
	//妈的 诈骗题 好坑啊  值得总结
	memset(ans, 0, sizeof ans);
	cin >> n >> k;
	int tot = 0;
	for (int i = 1; i <= n; i++)cin >> b[i];
	for (int i = 1; i <= n; i++)cin >> cost[i], tot += cost[i];
	//花费的价值
	int sum = 0;
	for (int i = 1; i <= n; i++) {
		sum += dp[b[i]];
	}
	if (k >= sum) {
		cout << tot << endl;
		return ;
	}
	int maxn=0;
	for (int i = 1; i <= n; i++) {
		for (int j = k; j >= dp[b[i]]; j--) {
			ans[j] = max(ans[j - dp[b[i]]] + cost[i], ans[j]);
			maxn=max(maxn,ans[j]);
		}
	}
	cout<<maxn<<endl;
//	cout << ans[k] << endl;
}
signed main()
{
	ios::sync_with_stdio();
	cin.tie(0);
	cout.tie(0);
	memset(dp, 0x3f, sizeof dp);
	dp[1] = 0;
	for (int i = 1; i <= 1e3 + 10; i++) {
		for (int j = 1; j <= i; j++) {
			dp[i + i / j] = min(dp[i + i / j], dp[i] + 1);
		}
	}
	int t;
	cin >> t;
	while (t--)
		solve();
	return 0;
	
	
}

状态压缩dp

这个题 我思考错了 我也想了一个512*n的做法 不过 我后面就思考到了
还是那句话 没有全局观 其实dp数组交代不清楚

/	for (int i = 3; i <= n; i++) {
//		int temp = now;
//		bool flag = 0;
//			for (int k = 0; k <= 520; k++) {
//				if ( dp[i][k] && !flag) {
//					now =  k | temp;
//					flag = 1;
//				}
//				else if (dp[i][k] && flag)
//					now = min(now, k | temp);
//			}	

这里的dp数组是那个到i可以成多少的意思 然后就可以了
这种dp开法也是很常见的 说实话

	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
		int w=a[i]&b[j];
		for(int k=0;k<=512;k++)
		dp[i][k|w]|=dp[i-1][k];			
		}
	}
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
#define debug cout<<endl<<"----------"<<endl;
using namespace std;
const int range=1000+10;
int n;int m;
int b[range];
int a[range];
int dp[300][range];
void solve()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++)cin>>b[i];
	dp[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int w=a[i]&b[j];
			for(int k=0;k<=512;k++)
				dp[i][k|w]|=dp[i-1][k];			
		}
	}
	for(int i=0;i<=512;i++)
	{
		if(dp[n][i]){
			cout<<i<<endl;return ;
		}
	}
}
signed main()
{
	ios::sync_with_stdio();
	cin.tie(0);
	cout.tie(0);
	solve();
	return 0;
	
	
} 

posted @ 2024-08-09 21:24  LteShuai  阅读(39)  评论(0)    收藏  举报