洛谷普及场(更要技巧的动规与记忆化)

  • P1064 金明的预算方案
    有依赖(分组)的背包问题,主件可以单独买,可以和一个附件,两个附件,依次判断即可
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std ; 
const int N = 65 ; 
const int M = 32010 ;  
pair<int,int> a[N] ; 
vector<pair<int,int>> b[N] ; 
int f[M] ; 
int main(){
	int n , m ;
	int p , v , w ; 
	scanf ("%d%d",&m,&n) ;
	for (int i = 1 ; i <= n ; ++i){
		scanf ("%d%d%d",&v,&w,&p) ;
		w *= v ; 
		if (!p){	//若为主件 
			a[i] = {v,w} ; 
		} 
		else{
			b[p].push_back({v,w}) ; 
		} 
	}  
	for (int i = 1 ; i <= n ; ++i){
		if (!a[i].first)	continue ; //若为附件 
		for (int j = m ; j >= 0 ; --j){
			for (int k = 0 ; k < 1<<b[i].size() ; ++k){	//x个附件和主件的组合情况有2的x次方种 
				int x = a[i].first , y = a[i].second ; //主件的价钱和价值
				for(int h = 0 ; h < b[i].size() ; ++h){
					if (k >> h & 1){//每种组合都是互斥的,从中选出一种(这里是参考网上大佬的方法,有点玄学。。。。)
						x += b[i][h].first ; 
						y += b[i][h].second ; 
					}
				} 
				if (j >= x)	f[j] = max(f[j] , f[j-x]+y) ; 
			}
		}	
	}
	printf ("%d\n",f[m]) ; 
	return 0 ; 
}
  • p1541 乌龟棋
#include <cstdio> 
#include <algorithm>
using namespace std ; 
const int N = 355 ;
const int M = 45 ;  
int maze[N] , card[5] ;
int dp[M][M][M][M] ;  
int main(){
	int n , m ; 
	scanf ("%d%d",&n,&m) ;
	for (int i = 1 ; i <= n ; ++i)	scanf ("%d",&maze[i]) ;
	for (int i = 1 ; i <= m ; ++i)	{
		int t ; 
		scanf ("%d",&t) ;
		card[t] ++ ; 
	}
	dp[0][0][0][0] = maze[1] ; 
	for (int a = 0 ; a <= card[1] ; ++a){
		for (int b = 0 ; b <= card[2] ; ++b){
			for (int c = 0 ; c <= card[3] ; ++c){
				for (int d = 0 ; d <= card[4] ; ++d){
					int score = maze[a + b*2 + c*3 + d*4 + 1] ;	//是从1开始出发走到第几格为步数加初始格数(即1)
					//要判断是否为0,否则会越界 
					if (a)  dp[a][b][c][d] = max(dp[a][b][c][d],dp[a-1][b][c][d]+score) ;
					if (b)  dp[a][b][c][d] = max(dp[a][b][c][d],dp[a][b-1][c][d]+score) ;
					if (c)  dp[a][b][c][d] = max(dp[a][b][c][d],dp[a][b][c-1][d]+score) ;
					if (d)  dp[a][b][c][d] = max(dp[a][b][c][d],dp[a][b][c][d-1]+score) ;
				}
			}
		}
	}
	printf ("%d\n",dp[card[1]][card[2]][card[3]][card[4]]) ; 
	return 0 ; 
}
  • p1063 能量项链

类似于环形石子的合并,只是最后的计算不同

#include <cstdio>
#include <algorithm>
using namespace std ; 
const int N = 210 ; 
int a[N] , f[N][N] ; 
int main(){
	int n ; 
	scanf("%d",&n) ;
	for (int i = 1 ; i <= n ; i ++){
		scanf ("%d",&a[i]) ;
		a[i+n] = a[i] ; 
		f[i][i] = 0 ;  
	} 
	for (int len = 1 ; len < n ; ++len){
		for (int i = 1 ; i <= 2*n-len ; ++i){
			int j = i + len ; 
			f[i][j] = 0 ; 
			for (int k = i ; k < j ; ++ k){
				f[i][j] = max(f[i][j] , f[i][k] + f[k+1][j] + a[i] * a[k+1] * a[j+1]) ; 
			}
		}
	}
	int ans = 0 ; 
	for (int i = 1 ; i <= n ; ++ i)
		ans = max(ans,f[i][i-1+n]) ; 
	printf ("%d\n",ans) ; 
	return 0 ; 
} 
  • p1156垃圾陷阱

填或者是吃,感觉有种背包的味道,emmmm但是这个状态转移又有些难想,
f[i][j]表示当扔下第ii个垃圾时,高度为jj此时的还可以存活多久
对于转移,分两种情况:
当不选用这个垃圾来当垫子时:
f[i+1][j]=max(f[i+1][j],f[i][j]+num[i].f)f[i+1][j]=max(f[i+1][j],f[i][j]+num[i].f)
当选用这个垃圾来当垫子时
f[i+1][j+h[i]]=max(f[i+1][j+num[i].h],f[i][j])f[i+1][j+h[i]]=max(f[i+1][j+num[i].h],f[i][j])
在当前高度大于所求的高度时可以打印出时间并跳出
当爬不出时:
ans=max(ans,f[i][0])(1<=i<=n)
想法来自于下方大佬题解:
康康大佬的题解:https://www.luogu.org/blog/27-43wyy/solution-p1156

#include <cstdio>
#include <algorithm>
using namespace std ; 
const int N = 110 ; 
struct node{
	int t , f , h ; 
}p[N] ; 
bool cmp(node a , node b){
	return a.t < b.t ; 
}
int f[N][N] ; 
int main(){
	int n , high ;
	scanf ("%d%d",&high,&n) ;
	for (int i = 1 ; i <= n ; ++ i)		scanf("%d%d%d",&p[i].t,&p[i].f,&p[i].h) ; 
	sort(p+1,p+1+n,cmp) ;
	f[0][0] = 10 ;
	for (int i = 0 ; i < n ; ++ i){
		for(int j = 0 ; j <= high ; ++ j){
			if (f[i][j] >= p[i+1].t){
				int h = j + p[i+1].h ;
				if (h >= high){
					printf ("%d\n",p[i+1].t) ; 
					return 0 ; 
				} 
				//当吃这个垃圾时 
				f[i+1][j] = max(f[i+1][j],f[i][j]+p[i+1].f) ; 
				//当用垃圾来当垫子时
				f[i+1][h] = max(f[i+1][h],f[i][j]) ;  
			}
		} 
	} 
	int ans = 0  ;
	for (int i = 1 ; i <= n ;++ i)
		ans = max(ans,f[i][0]) ; 
	printf ("%d\n",ans) ;
	return 0 ; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值