191031-基础测试(dp+搜索剪枝)

本文深入解析了三道算法竞赛题目:农场实习、运输方案与最佳调度问题,介绍了如何运用动态规划、搜索剪枝等技术解决复杂问题,通过具体代码示例展示了算法实现过程。

191031-基础测试(dp+搜索剪枝)

T1 农场实习

解析

由于数据范围,所以考虑 O ( n l o g n ) O(nlogn) O(nlogn)的算法来求最长不下降子序列

题解

#include<bits/stdc++.h>
using namespace std;
int read()
{
	int f=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-')
	{
		f=-1;
		ch=getchar();
	}
	for(;isdigit(ch);ch=getchar())
		re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int n,a[1000009],len,b[1000009];
int main()
{
	//freopen("farm.in","r",stdin);
	//freopen("farm.out","w",stdout);
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		a[i]=read();
	b[0]=a[0];
	len=1;
	for(int i=1;i<n;i++)
	{
		if(a[i]>=b[len-1])
			b[len++]=a[i];
		else
			*upper_bound(b,b+len,a[i])=a[i];
	}
	printf("%d",n-len);
	return 0;
}

T2 运输方案

解析

搜索剪枝
1,可行性剪枝,超重了直接return
2,最优化剪枝,如果当前 maxn+还没选的货物的价值总和 都已经小于或等于ans了,直接return

题解

#include<bits/stdc++.h>
using namespace std;
int n;
double a[34],b[34],sum[34],m,ans;
bool bj[34],vis[34];
void dfs(int cur,double maxn,double wei)
{
	if(wei>m) return;
	if(maxn+sum[cur]<=ans)
		return;
	if(cur==n+1)
	{
		if(ans<maxn)
		{
			ans=maxn;
			for(int i=1;i<=n;i++)
				bj[i]=vis[i];
		}
		return;
	}
	vis[cur]=1;
	dfs(cur+1,maxn+b[cur],wei+a[cur]);
	vis[cur]=0;
	dfs(cur+1,maxn,wei);
	return;
}
int main()
{
	scanf("%lf%d",&m,&n);
	for(int i=1;i<=n;i++)
		scanf("%lf%lf",&a[i],&b[i]);
	for(int i=n;i>=1;i--)
		sum[i]=sum[i+1]+b[i];
	dfs(1,0,0);
	printf("%d\n",int(ans));
	if(int(ans)==0) return 0;
	for(int i=1;i<=n;i++)
		if(bj[i]) printf("%d ",i);
	return 0;
}

T3 最佳调度问题

解析

搜索剪枝
1,按任务的时间大小降序排序,这样可以快速去掉无用分支
2,最优化剪枝,如果当前要花的时间,已经超过ans了,直接continue

解析

#include<bits/stdc++.h>
using namespace std;
int used[100],a[100],ans,n,p;
bool comp(const int &a,const int &b)
{
	return a>b;
}
void dfs(int cur,int time)
{
	if(time>=ans)
		return;
	if(cur==n+1)
	{
		ans=min(time,ans);
		return;
	}
	for(int i=1;i<=p;i++)
	{
		if(used[i]+a[cur]>=ans) continue;
		used[i]+=a[cur];
		dfs(cur+1,max(time,used[i]));
		used[i]-=a[cur];
	}
}
int main()
{
	scanf("%d%d",&n,&p);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	sort(a+1,a+n+1,comp);
	ans=1e8;
	dfs(1,0);
	printf("%d",ans);
	return 0;
}
为了让你的 **DFS 解法** 在小数据(如 `n, m ≤ 20`)中尽可能通过更多测试点,我们可以在你当前的 DFS 基础上加入一些**剪枝策略**,以减少不必要的搜索路径。 --- ### ✅ 加入剪枝策略 1. **记忆化剪枝(关键剪枝)**: 记录 `(x, y)` 位置上已经到达过的最大路径和,如果当前路径和小于等于之前记录的值,直接剪枝。 2. **贪心预估剪枝(乐观估计剪枝)**: 预估从当前位置 `(x, y)` 到终点 `(n, m)` 所能获得的最大价值(比如所有未走过的格子都是正数),如果加上当前 `tot` 仍小于当前最优解,剪枝。 3. **优先尝试高价值方向剪枝**: 按照从上、下、右的顺序尝试搜索,优先尝试可能带来更高收益的方向(比如向右)。 --- ### ✅ 带剪枝的 DFS 代码(适用于 `n, m ≤ 20`) ```cpp #include <bits/stdc++.h> using namespace std; const int N = 25; const int INF = 0x3f3f3f3f; int n, m; int a[N][N]; int best[N][N]; // 记忆化数组:best[x][y] 表示到达(x,y)的最大价值 bool v[N][N]; // 是否已访问 int ans = -INF; int dx[3] = {1, -1, 0}; int dy[3] = {0, 0, 1}; // 乐观估计函数:从(x,y)(n,m)最多还能获得多少分数(假设所有未访问格子都是正数) int optimistic(int x, int y) { int total = 0; for (int i = x; i <= n; ++i) for (int j = (i == x ? y : 1); j <= m; ++j) if (!v[i][j]) total += max(0, a[i][j]); return total; } void dfs(int x, int y, int tot) { // 剪枝1:当前路径和 + 乐观估计 <= 当前最优解,剪枝 if (tot + optimistic(x, y) <= ans) return; // 剪枝2:记忆化剪枝,如果当前路径和小于等于之前记录的 best[x][y],剪枝 if (best[x][y] >= tot) return; best[x][y] = tot; // 到达终点 if (x == n && y == m) { ans = max(ans, tot); return; } // 优先尝试向右走(因为只能向右推进列) if (y + 1 <= m && !v[x][y+1]) { v[x][y+1] = true; dfs(x, y+1, tot + a[x][y+1]); v[x][y+1] = false; } // 再尝试上下走 for (int i = 0; i < 2; ++i) { int nx = x + dx[i]; int ny = y + dy[i]; if (nx < 1 || nx > n || ny < 1 || ny > m || v[nx][ny]) continue; v[nx][ny] = true; dfs(nx, ny, tot + a[nx][ny]); v[nx][ny] = false; } } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) cin >> a[i][j]; // 初始化 best 为极小值 memset(best, -0x3f, sizeof(best)); v[1][1] = true; dfs(1, 1, a[1][1]); cout << ans << "\n"; return 0; } ``` --- ### ✅ 剪枝效果说明 | 剪枝类型 | 作用 | |----------|------| | 记忆化剪枝 | 避免重复路径,提升效率 | | 乐观估计剪枝 | 提前剪掉不可能更优的路径 | | 优先方向剪枝 | 更快找到高质量路径,利于剪枝 | | 限制搜索范围 | 只用于 `n, m ≤ 20` 的小数据 | --- ### ❗ 注意事项 - 本代码适用于 `n, m ≤ 20` 左右的小数据,不能处理 `1000×1000` 的大数据。 - 对于 `n, m ≤ 100` 的数据,建议使用动态规划。 - 对于 `n, m ≤ 1000` 的完整数据,必须使用 DP + 双向更新。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值