石子归并 经典的区间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)
(ai≤1000)。
由于是很低端的餐馆,所以每种菜只有一份。
小
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,你能完成这个任务吗?
此题和原题的不同点:
- 每种草药可以无限制地疯狂采摘。
- 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!
输入格式
输入第一行有两个整数,分别代表总共能够用来采药的时间
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
说明/提示
数据规模与约定
-
对于 30% 的数据,保证 m ≤ 1 0 3 m≤10^3 m≤103 。
-
对于 100% 的数据,保证 1 ≤ m ≤ 1 0 4 1≤m≤10^4 1≤m≤104, 1 ≤ t ≤ 10 1≤t≤10 1≤t≤10,且 m × t m×t m×t < 1 0 7 10^7 107, 1 ≤ a i 1 ≤a_i 1≤ai, b i ≤ 1 0 4 ≤ a b_i≤10^4≤a bi≤104≤a。
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;
}