SMZX夏令营10日游(第一阶段DP):石子归并,小A点菜,All in All,疯狂的采药代码详解

石子归并 经典的区间DP

题目描述

在一个圆形操场的四周摆放 N \rm N N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 N \rm N N 堆石子合并成 1 1 1 堆的最小得分和最大得分。

输入格式

数据的第 1 1 1行是正整数 N \rm N N,表示有 N \rm N N 堆石子。
2 2 2行有 N \rm N N个整数,第 i \rm i i个整数 a i a_i ai表示第 i i\rm i堆石子的个数。

输出格式

输出共 2 2 2 行,第 1 1 1 行为最小得分,第 2 2 2 行为最大得分。

输入输出样例

输入 #1

4 4 4
4 4 4 5 5 5 9 9 9 4 4 4

输出 #1

43 43 43
54 54 54

说明/提示

1 1 1 ≤ ≤ N \rm N N ≤ ≤ 100 100 100 0 0 0 ≤ ≤ a i a_i ai ≤ ≤ 20 20 20

Code

#include<bits/stdc++.h> //万能头
using namespace std; 
int n,f2[1006][1006],f1[1006][1006],num[1006],s[1006],maxx,minn;//定义
int sum(int i,int j)//计算i~j石子的数量
{
	return s[j]-s[i-1];
}
int main()  
{ 
cin>>n;//普普通通的输入
for(int i=1;i<=2*n;i++)//2倍拆环。注:测试样例最好用freopen或洛谷的在线IDE,否则手动输入样例你会发现不得了的东西……
{
	cin>>num[i];//普普通通的输入+1
	num[i+n]=num[i];//多copy一个环
	s[i]=s[i-1]+num[i];//前缀和
}
for(int p=1;p<n;p++)//防止越界 
{
    for(int i=1,j=p+i;(j<2*n) && (i<2*n);i++,j=i+p)//设置i~jDP范围,i是左边,就是右边
    {
    	f1[i][j]=-1;//求最大值设最小值
		f2[i][j]=1e9;//同上,求最小值设最大值
    	for(int k=i;k<j;k++)//枚举分割点
    	{
    		f1[i][j]=max(f1[i][j],f1[i][k]+f1[k+1][j]+sum(i,j));//动态转移方程,f1[i][j]为不选,f1[i][k]为i~k的合并得到的得分,f1[k+1][j]同上,k+1~j的合并得到的得分
    		f2[i][j]=min(f2[i][j],f2[i][k]+f2[k+1][j]+sum(i,j));//同上,不在啰嗦
    	}
    }
}
maxx=-1;//求最大值设最小值
minn=1e9;//同上,求最小值设最大值
for(int i=1;i<=n;i++) maxx=max(maxx,f1[i][i+n-1]);//在f1这里找最大,本处用的是打擂台
for(int i=1;i<=n;i++) minn=min(minn,f2[i][i+n-1]);//同上,在f2里找最小
cout<<minn<<endl<<maxx;//输出
    return 0;  //完结撒花
}

小A点菜 非常简单的01背包

题目背景

u i m \rm uim uim神犇拿到了 u o i \rm uoi uoi r a \rm ra ra(镭牌)后,立刻拉着基友小 A \rm A A到了一家餐馆,很低端的那种。
u i m \rm uim uim指着墙上的价目表(太低级了没有菜单),说:“随便点”。

题目描述

不过 u i m \rm uim uim由于买了一些辅 ( e ) \rm (e) e ( r o ) \rm (ro) ro书,口袋里只剩 M \rm M M元(M≤10000)。
餐馆虽低端,但是菜品种类不少,有 N \rm N N种(N≤100),第 i \rm i i种卖 a i \rm a_i ai ( a i ​ ≤ 1000 ) \rm (a_i​ ≤1000) (ai1000)
由于是很低端的餐馆,所以每种菜只有一份。
A \rm A A奉行“不把钱吃光不罢休”,所以他点单一定刚好把 u i m \rm uim uim身上所有钱花完。他想知道有多少种点菜方法。
由于小 A \rm A A肚子太饿,所以最多只能等待 1 1 1秒。

输入格式

第一行是两个数字,表示 N \rm N N M \rm M M
第二行起 N \rm N N个正数 a i a_i ai
(可以有相同的数字,每个数字均在 1000 1000 1000以内)。

输出格式

一个正整数,表示点菜方案数,保证答案的范围在int之内。

输入输出样例

输入 #1

4 4 4 4 4 4
1 1 1 1 1 1 2 2 2 2 2 2

输出 #1

3 3 3

Code

//小A点菜(01背包) (选与不选)
#include<bits/stdc++.h>//万能头 
using namespace std;
long long m,f[10000000],W,w[10000],v[10000];//定义 
int main()
{
	cin>>m>>W;//普普通通的输入 
	for(int i=1;i<=m;i++)
	cin>>w[i];
	f[0]=1;//初始化(小心爆0,之所以设置1,是因为当没有菜时,只能不选) 
	for(int i=1;i<=m;i++)
		for(int j=W;j>=w[i];j--)//01背包标准模板 
	        f[j]+=f[j-w[i]];//(累加)
	 cout<<f[W];   
    return 0;//完结撒花
}

All in All (无DP版,指针版本)

题目描述

读入两个字符串s和t,问是否能通过删去串t中的某几个字符得到串s,(大小写区分),如果能则输出 Yes,否则输出 No。

输入输出样例

输入 #1

s e q u e n c e s u b s e q u e n c e \rm sequence subsequence sequencesubsequence
p e r s o n c o m p r e s s i o n \rm person compression personcompression
V E R D I v i v a V i t t o r i o E m a n u e l e R e D i I t a l i a \rm VERDI vivaVittorioEmanueleReDiItalia VERDIvivaVittorioEmanueleReDiItalia
c a s e D o e s M a t t e r C a s e D o e s M a t t e r \rm caseDoesMatter CaseDoesMatter caseDoesMatterCaseDoesMatter

输出 #1

Y e s \rm Yes Yes
N o \rm No No
Y e s \rm Yes Yes
N o \rm No No

Code

#include<iostream>
#include<string>  
using namespace std;  
string s,st; 
int n,m,v,p,q,f[100000],zv[100000],zw[100000],fw[100000][5],fv[100000][5];
int main()  
{ 
    while(cin>>st>>s)//多组输入
    {
    	int i=0,j=0,s_end=s.size(),st_end=st.size();//设定指针
    	while(1)//一直寻找,找到为止
    	{
    		if(st[i]==s[j])//匹配2个字母,如果匹配成功,则寻找下一个字母
    	  {
    		   i++;//移动
			   j++;
    		   if(i==st_end)//如果全部匹配成功,退出
    		 {
    			cout<<"Yes"<<endl;
    			break;
    		 }
    	  }
    	else
    	 {
    		j++;//如果2个字母无法成功匹配,那就在长串选下一个字母再找一次
    		if(j==s_end)//如果全部匹配失败,退出
    		{
    			cout<<"No"<<endl;
    			break;
    		}
    	 }
    	}
		
    }
    return 0;  
}

疯狂的采药 经典完全背包

题目背景

此题为纪念 L i Y u x i a n g \rm LiYuxiang LiYuxiang 而生。

题目描述

L i Y u x i a n g \rm LiYuxiang LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是 L i Y u x i a n g \rm LiYuxiang LiYuxiang,你能完成这个任务吗?
此题和原题的不同点:

  1. 每种草药可以无限制地疯狂采摘。
  2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

输入格式

输入第一行有两个整数,分别代表总共能够用来采药的时间 t \rm t t 和代表山洞里的草药的数目 m \rm m m
第2到第 ( m + 1 ) \rm (m+1) (m+1) 行,每行两个整数,第 ( i + 1 ) \rm (i+1) (i+1) 行的整数 a i \rm a_i ai, b i \rm b_i bi分别表示采摘第 i \rm i i 种草药的时间和该草药的价值。

输出格式

输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

输入输出样例

输入 #1

70 70 70 3 3 3
71 71 71 100 100 100
69 69 69 1 1 1
1 1 1 2 2 2

输出 #1

140 140 140

说明/提示

数据规模与约定

  1. 对于 30% 的数据,保证 m ≤ 1 0 3 m≤10^3 m103

  2. 对于 100% 的数据,保证 1 ≤ m ≤ 1 0 4 1≤m≤10^4 1m104 1 ≤ t ≤ 10 1≤t≤10 1t10,且 m × t m×t m×t < 1 0 7 10^7 107 1 ≤ a i 1 ≤a_i 1ai, b i ≤ 1 0 4 ≤ a b_i≤10^4≤a bi104a

Code

#include<bits/stdc++.h>
using namespace std;
long long m,f[10000000],W,w[10000],v[10000];
int main()
{
	cin>>W>>m;
	for(int i=1;i<=m;i++)
	cin>>w[i]>>v[i];//普普通通的输入
	for(int i=1;i<=m;i++)
	    for(int j=w[i];j<=W;j++)//完全背包基本模板
	        f[j]=max(f[j-w[i]]+v[i],f[j]);
	cout<<f[W];
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值