目录
方块与收纳盒
舔狗舔到最后一无所有
可爱の星空
数字三角形
花店橱窗
[NOI1998]免费馅饼
[NOIP2002]过河卒
[NOIP2008]传球游戏
「木」迷雾森林
[NOIP2004]合唱队形
[NOIP1999]拦截导弹
数学考试
小A买彩票
购物
牛牛的旅游纪念品
[NOIP2001]装箱问题
[NOIP2005]采药
[NOIP2006]开心的金明
CSL分苹果
失衡天平
[NOIP2006]金明的预算方案
队伍配置
取数游戏2
石子合并
石子合并
[NOIP2008]传纸条
[NOIP2000]方格取数
[NOIP2010]乌龟棋
[NOIP2006]能量项链
[NOIP2018]货币系统
方块与收纳盒
预处理之后很简单,已知0,1,2的方案数,那么之后每次都是i-1的方案数+i-2的方案数。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=100;
int dp[N];
void solve() {
int n;
cin>>n;
cout<<dp[n]<<endl;
}
signed main() {
ios::sync_with_stdio ( false );cin.tie ( nullptr ); cout.tie ( nullptr );
int oyyo = 1;
dp[0]=1;
dp[1]=1;
dp[2]=2;
for(int i=3;i<=100;i++){
dp[i]=dp[i-1]+dp[i-2];
}
cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
舔狗舔到最后一无所有
因为不能连续三天吃一样的东西,所以可以先假设第一天吃A,然后情况就是第i-1天吃A,第i-1天吃B,第i-1天吃C,其中第i-1天吃A时,第i-2天不能吃A,只能吃B和C。
所以第i天吃A的情况就是第i-2天吃B,第i-2天吃C,第i-1天吃B,第i-1天吃C。
对于第i天吃B,C的情况和吃A的情况一样。
B:第i-2天吃A,第i-2天吃C,第i-1天吃A,第i-1天吃C。
C:第i-2天吃A,第i-2天吃B,第i-1天吃A,第i-1天吃B。
所以最后第i天的情况总和为。
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=100005;
const int mod=1e9+7;
int dp[N];
void solve() {
int n;
cin>>n;
cout<<dp[n]<<endl;
}
signed main() {
ios::sync_with_stdio ( false );cin.tie ( nullptr );cout.tie ( nullptr );
int oyyo = 1;
dp[0]=1;
dp[1]=3;
dp[2]=9;
for(int i=3;i<N;i++){
dp[i]=dp[i-1]%mod*2%mod+dp[i-2]*2%mod;
dp[i]%=mod;
}
cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
可爱の星空
给了你n个点,可以连成图,u和v两个小图连图的代价是 |u连通块的大小-v连通块的大小| (绝对值),问你最终连成一个大块的最小代价是什么。
挺牛逼的题,被吓了。
他的范围会有一些很大的,所以一时间不知道怎么搞。问题的解法是单一的,对于一个数,最小代价的解法就是尽量平分成两个相同大小,奇数就是 n/2 和 n/2+1,偶数就是两个 n/2,当n==1,n==2时,都直接返回0,状态方程很好写,但当面对大数就很难堪,其实只要稍微有一些改写就好了。
如果你让这里再去递归两次就会超时,所以直接乘2就好。这个每次都除二,大胆写就好。我就是没胆量。
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=100005;
const int mod=1e9+7;
int answ(int n){
if(n==1)
return 0;
if(n==2){
return 0;
}
if(n%2==1){
return answ(n/2)+answ(n/2+1)+1;
}else{
return answ(n/2)*2;//**there。**//
}
}
void solve() {
int n;
cin>>n;
cout<<answ(n)<<endl;
}
signed main() {
ios::sync_with_stdio ( false );cin.tie ( nullptr );cout.tie ( nullptr );
int oyyo = 1;
cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
数字三角形
照他说的做。
第一个数的公式是。
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=100005;
const int mod=1e9+7;
void solve() {
int n;
cin>>n;
for(int i=1;i<=n;i++){
int k;
k=i*(i+1)/2-(i-1);
for(int j=1;j<=i;j++){
printf("%4d",k+j-1);
}
cout<<endl;
}
}
signed main() {
//ios::sync_with_stdio ( false );cin.tie ( nullptr );cout.tie ( nullptr );
int oyyo = 1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
花店橱窗
这个❀首先要满足题目的要求,每个花都要按原编号顺序从左到右排列。所以第一个花最多摆到末尾位置-f+1,第 i 个花最前摆到 i 位置。
我们可以这样做。对于第 x,y 位置,它可以继承 x-1行,(x~y-1)的所有情况,所以我们的dp数组就去记录在每个位置下,他最多的美观度是多少。首先对第一行,如果当前的花 A 加上花瓶构成的美观度,小于这一行目前出现的最大值,那么我们就直接记录最大值就好,因为对于下一行(下一个要判断的花),我们如果会再考虑现在这个花 A ,当且仅当在下一行我们会走到A花之后的位置,这样才会去讨论 A 的价值,但在这个情况下,我们完全可以选择A花之前的最优情况,所以直接去记录目前为止的最大值就好。对于之后的行也是如此,如果当前花A(x,y)的价值+dp[x-1][y-1]的价值(因为dp[x-1][y-1] 就是在这个位置可以选到的最大价值了)小于这一行的上一个位置dp[x][y-1],那么就去继承他左边的位置。否则就是他自己又产生了新的最大值。
坐标的输出比较搞,我搜的。但不难。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=105;
const int mod=1e9+7;
int a[N][N];
int dp[N][N];
int d[N];
void solve() {
int f,v;//一共有F中❀,V个花瓶。
cin>>f>>v;
for(int i=1;i<=f;i++){
for(int j=1;j<=v;j++){
cin>>a[i][j];
dp[i][j]=-inf;//初始化dp数组,以为会存在负数,为了让dp[i]记录当前可能的最大情况就要初始化为负无穷。
}
}
int maxn=a[1][1];
for(int i=1;i<=v-f+1;i++){
if(a[1][i]>maxn){
dp[1][i]=a[1][i];
maxn=a[1][i];
//首先先去找第一行的最大可能情况。如果他不够大,他可以继承前面的最大情况。
}else{
dp[1][i]=maxn;
}
}
for(int i=2;i<=f;i++){
for(int j=i;j<=v-f+i;j++){//每一行只能从第i个开始选,因为要满足题目所给“左右一致”
//之后哦每一行,他要么是继承前一个的最大值。
dp[i][j]=max(dp[i][j-1],dp[i-1][j-1]+a[i][j]);
}
}
//找到后输出最大值。
cout<<dp[f][v]<<endl;
//然后回溯去寻找序列。
int ans=dp[f][v];
int cnt=f;
for(int i=f;i>=1;i--){
for(int j=1;j<=v;j++){
if(dp[i][j]==ans){
d[cnt--]=j;//当他找到和要要寻找的数等大的。就直接传递回上一层,这样字典序最小。
ans-=a[i][j];//找到后减去这一行的最大值位置。就是上一行所能产生的最大值了。
break;
}
}
}//出
for(int i=1;i<=f;i++){
cout<<d[i]<<" ";
}
}
signed main() {
ios::sync_with_stdio ( false );cin.tie ( nullptr );cout.tie ( nullptr );
int oyyo = 1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
[NOI1998]免费馅饼
[NOIP2002]过河卒
因为只能向下或向右去走,所以到一个点只有通过它左边的路或上边的路。
那么到某点的路径条数=到它左边点的条数+到它上面点的条数(前提是这两点都可到)。
真正到边线上的点的路径条数应该是他的上一个点的条数(上边界就是它左边的点,左边姐就是它上边的点)而不是简单的规定为1。还有特殊的就是原点了。我们把它规定路径条数为1。
用数组记录马控制的区域表示此路不通。在循环判断到这个点后直接跳过就好。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
void solve(){
int n,m,p,q;
int a[30][30]={0};
int map[30][30]={0};
cin>>n>>m>>p>>q;
map[p][q]=1;
map[p-2][q-1]=1;
map[p-2][q+1]=1;
map[p+2][q+1]=1;
map[p+2][q-1]=1;
map[p-1][q-2]=1;
map[p-1][q+2]=1;
map[p+1][q+2]=1;
map[p+1][q-2]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
if(map[i][j]){
continue;
}else{
if(i==0 and j==0){
a[i][j]=1;
}else if(i==0 and j>0){
a[i][j]=a[i][j-1];
}else if(j==0 and i>0){
a[i][j]=a[i-1][j];
}else{
a[i][j]=a[i-1][j]+a[i][j-1];
}
}
}
}
cout<<a[n][m]<<endl;
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int t=1;
//cin>>t;
while(t--) {
solve();
}
return 0;
}
[NOIP2008]传球游戏
每个同学可以从左或右边同学接球,即当前这一轮中间同学可以拿到球的总方法=上一轮左同学的可拿球方法+上一轮右同学的可拿球方法。
状态转移方程 dp[i][j] = dp[i-1][j-1]+dp[i-1][j+1];
题目说一开始从小蛮开始,那么一开始小蛮就得到了一个球,只有这一个方法可以得到球。
记住边界要特殊判断。就好。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=35;
int dp[N][N];
void solve(){
int n,m;//N个人,传了M次后
cin>>n>>m;
dp[0][1]=1;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(j==1){
dp[i][j]=dp[i-1][n]+dp[i-1][2];
}else if(j==n){
dp[i][j]=dp[i-1][j-1]+dp[i-1][1];
}else{
dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1];
}
}
}
cout<<dp[m][1]<<endl;
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int t=1;
//cin>>t;
while(t--) {
solve();
}
return 0;
}
「木」迷雾森林
刚开始在左下方,要求只能向上或向右走,走到右上方,有1的地方不能走,求方案数对2333取模。
我们把问题分解成加法问题,开一个数组s,s【i,j】表示从左下走到i,j这个点的方案数是多少,当前的点i,j是由下方【i+1,j】和左边【i,j-1】的方案数相加,而【n,1】的方案数是1,我们遇到1的时候直接令这个点方案数为0就可以了,从下往上dp,最后输出s【1,n】%2333即可。
dp [ i] [ j ] = dp [ i - 1 ] [ j ] + d p [ i ] [ j - 1 ]
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=5005;
const int mod=1e9+7;
int a[3005][3005];
int s[3005][3005];
template<class T>inline void read(T &res)
{
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
void solve() {
int n,m;
memset(s,0,sizeof s);
read(n),read(m);
s[n][1]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
read(a[i][j]);
}
}
for(int i=n;i>=1;i--){
for(int j=1;j<=m;j++){
if(i==n and j==1){
continue;
}
if(a[i][j]==1){
s[i][j]=0;
continue;
}
s[i][j]=s[i+1][j]%2333+s[i][j-1]%2333;
s[i][j]%=2333;
}
}
cout<<s[1][m]<<endl;
}
signed main() {
ios::sync_with_stdio ( false );cin.tie ( nullptr );cout.tie ( nullptr );
int oyyo = 1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
[NOIP2004]合唱队形
本题是要求出从 第一个人到第i个最高的人的最长上升子序列 以及从第i个人到第n个人的最长下降子序列(其实就是第n个人反向到第i个人的最长上升子序列)
建立二维数组dp[105][2]。
dp[i][0]代表以第i个人为结尾的最长上升子序列长度。(整个合唱队形没有下降)
dp[i][1]代表以第i个人为结尾的最长合唱队形,但至少有一个人的身高呈下降趋势(合唱队形有下降)
显然,max(a[i][0],a[i][1])代表前i个人的最长合唱队形。
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=100005;
const int mod=1e9+7;
int dp[105][2];
void solve() {
int n;
cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
dp[i][0]=1;
for(int j=1;j<i;j++){
if(a[i]>a[j]){
dp[i][0]=max(dp[i][0],dp[j][0]+1);
}
}
}
for(int i=1;i<=n;i++){
dp[i][1]=1;
for(int j=1;j<i;j++){
if(a[i]<a[j]){
dp[i][1]=max(dp[i][1],max(dp[j][0],dp[j][1])+1);
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,max(dp[i][0],dp[i][1]));
}
cout<<n-ans<<endl;
}
signed main() {
ios::sync_with_stdio ( false );cin.tie ( nullptr );cout.tie ( nullptr );
int oyyo = 1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
[NOIP1999]拦截导弹
真的没搞懂。。。。。。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=100005;
const int mod=1e9+7;
int f[N];
int a[N];
int n,t;
void solve() {
while(cin>>a[++n]);
n--;
t=0;
memset(f,0,sizeof(f));f[0]=inf;
for(int i=1;i<=n;i++){
int l=0,r=t+1;
while(r-l>1){
int m=l+(r-l)/2;
if(f[m]>=a[i]){
l=m;
}else{
r=m;
}
}
int x=l+1;
if(x>t){
t=x;
}
f[x]=a[i];
}
cout<<t<<endl;
t=0,memset(f,0,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
int l=0,r=t+1;
while(r-l>1){
int m=l+(r-l)/2;
if(f[m]<a[i]){
l=m;
}else{
r=m;
}
}
int x=l+1;
if(x>t){
t=x;
}
f[x]=a[i];
}
cout<<t<<endl;
}
signed main() {
ios::sync_with_stdio ( false );cin.tie ( nullptr );cout.tie ( nullptr );
int oyyo = 1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
数学考试
先求前缀和,因为两个区间不相交,所以找一个基准点,找到该基准点左侧的sum最大的的区间(可以将该基准点视为在左区间),再在基准点右侧找到sum最大的区间,最后相加。
让每个数都做基准点,找到它们各自的左区间sum最大
- zuo[i] 为以 i 做基准点,左侧的sum最大区间。
- 因为要求的的区间长度为k,所以 i 要从 k开始,如果从 k 以前的数开始的话,区间长度根本都不满足题中要求的长度k。
让每个数都做基准点,找到它们各自的右区间sum最大
- you[i] 为以 i 做基准点,右侧的sum最大区间。
- i要从从右数k个数开始,即n - k + 1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f3f
#define pii pair<int,int>
const int mod=1e9+7;
const int N=200005;
int b[N];
int you[N];
int a[N];
void solve() {
int n,k;
cin>>n>>k;
memset(a,0,sizeof(a));
memset(you,-1e4,sizeof(you));
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
b[i]=b[i-1]+a[i];
}
for(int i=n-k+1;i>=1;i--){
you[i]=max(you[i+1],b[i+k-1]-b[i-1]);
}
int maxn=-1e18;
for(int i=k;i<=n-k;i++){
maxn=max(maxn,b[i]-b[i-k]+you[i+1]);
}
cout<<maxn<<endl;
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
小A买彩票
根据题目先给出概率公式:不亏本的数量/总数量。
因为每买一张彩票有4种方法,那么买n张彩票就有4的n次方种方法。
因为每张票3元,所以不亏本的金额范围只能为3n——4n。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f3f3f
#define pii pair<int,int>
const int mod=1e9+7;
const int N=200005;
int dp[35][200];
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
void solve() {
int n;
cin>>n;
int ans=0;
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=i;j<=4*i;j++){
for(int k=1;k<=4;k++){
if(j>=k){
dp[i][j]+=dp[i-1][j-k];
}
}
}
}
for(int i=3*n;i<=4*n;i++){
ans+=dp[n][i];
}
int sum=pow(4,n);
int gbs=gcd(ans,sum);
cout<<ans/gbs<<"/"<<sum/gbs<<endl;
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
购物
牛牛的旅游纪念品
这个题如果没有对位置的限制,就可以贪心取前m大的数即可。
但本题的关键在于买的m个物品中任意两个的位置差都大于等于k。那么贪心的方法显而易见行不通,很容易联想到用动态规划。
由题可知本题有两个状态,n个物品以及取走m个物品,并且对物品的位置有着限制,那么我们可以定义
dp[i][j]:前j个物品取i个的欢迎程度
dp[i][j]无非由两种状态转移而来
1. 没有买第j个物品,那么dp[i][j]=dp[i][j-1]。
2. 买了第j个物品,那么此时dp[i][j]=dp[i-1][j-k]+a[j]。
很容易得到状态转移 方程:dp[i][j]=max(dp[i][j-1],dp[i-1][j-k]+a[j])。
初始状态就是在取第一个时,dp[1][j]=max(dp[1][j-1],a[j])。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf1 0x3f3f3f3f
#define inf2 0x3f3f3f3f3f3f3f3f3f3f
#define pii pair<int,int>
const int mod=1e9+7;
const int N=25;
int dp[105][10005];
void solve() {
memset(dp,-inf1,sizeof(dp));
int n,m,k;
cin>>n>>m>>k;
vector<int>a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
dp[1][i]=max(dp[1][i-1],a[i]);
}
for(int i=2;i<=m;i++){
for(int j=k+1;j<=n;j++){
dp[i][j]=max(dp[i][j-1],dp[i-1][j-k]+a[j]);
}
}
cout<<dp[m][n]<<endl;
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
[NOIP2001]装箱问题
因为每个物体,都有装与不装两种选择,所以我们得到状态转移方程:
f[j]=max(f[j],f[j-w[i]]+w[i]);
f[j] 为:当总容量为 j 时,不放第 i 件物品,所能装的最大体积。
f[j-w[i]]+w[i] 为:当总容量为 j 时,放了第 i 件物品后,所能装的最大体积。(即 j减去第 i 件物品体积 的容量能装的最大体积+第 i 件物品的体积。w[i] 为第 i 件物品体积)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int N=100005;
const int mod=1e9+7;
int dp[20005];
int a[35];
void solve() {
int v;
cin>>v;
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
for(int j=v;j>=a[i];j--){
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
}
}
cout<<v-dp[v]<<endl;
}
signed main() {
ios::sync_with_stdio ( false );cin.tie ( nullptr );cout.tie ( nullptr );
int oyyo = 1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
[NOIP2005]采药
dp[i][j]=max(dp[i-1][j-a[i].t]+a[i].v,dp[i-1][j]);
#include<bits/stdc++.h>
#define endl '\n'
#define mk make_pair
//#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 3050;
const int mod = 2333;
struct cao{
int t,v;
};
int dp[105][1005];
void sovle(){
int t,m;
cin>>t>>m;
vector<cao>a(m+1);
for(int i=1;i<=m;i++){
cin>>a[i].t>>a[i].v;
}
for(int i=1;i<=m;i++){
for(int j=t;j>=0;j--){
if(j>=a[i].t){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i].t]+a[i].v);
}else{
dp[i][j]=dp[i-1][j];
}
}
}
cout<<dp[m][t]<<endl;
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(0),cout.tie(0);
int t = 1;
//cin>>t;
while (t--){
sovle();
}
return 0;
}
//template<class T>inline void read(T &res)
//{
// char c;T flag=1;
// while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
// while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
//}
[NOIP2006]开心的金明
定义一个二维数组dp,dp[i][j]表示买到第i个商品(不一定要全买),最多花j元钱。
如果当前的钱可以买就更新状态,否则记录上一次状态。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf1 0x3f3f3f3f
#define inf2 0x3f3f3f3f3f3f3f3f3f3f
#define pii pair<int,int>
const int mod=1e9+7;
const int N=25;
struct wu{
int v;
int cost;
};
int dp[30][30005];
void solve() {
int n,m;
cin>>n>>m;
//总钱数,希望购买的物品的个数。
vector<wu>a(m+1);
for(int i=1;i<=m;i++){
cin>>a[i].cost>>a[i].v;
}
for(int i=1;i<=m;i++){
for(int j=0;j<=n;j++){
dp[i][j]=dp[i-1][j];
if(j>=a[i].cost){
dp[i][j]=max(dp[i][j],dp[i-1][j-a[i].cost]+a[i].cost*a[i].v);
}
}
}
cout<<dp[m][n]<<endl;
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
CSL分苹果
重量即价值,背包最大容量就是总容量的一半
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf1 0x3f3f3f3f
#define inf2 0x3f3f3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define PI acos(-1.0)
const int mod=1e9+7;
const int N=25;
int dp[10005];
void solve() {
int n;
cin>>n;
vector<int>a(n+1);
int sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
}
for(int i=1;i<=n;i++){
for(int j=sum/2;j>=a[i];j--){
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
}
}
cout<<dp[sum/2]<<" "<<sum-dp[sum/2]<<endl;
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
//cin >> oyyo;
while ( oyyo-- ) {
solve();
}
return 0;
}
失衡天平
对于每个物品我们都要做出选择:放在天平的左边,放在天平的右边,不选择。
那么我们就可以去找递推式:
dp[i][j]表示前i样物品进行选择后,此时天平两端的重量差为j的时候,最大质量是多少,我们定义这个质量差为 左减右,那么左边放物品质量差增大,右边放质量差变小
(a[i]表示第i件物品的质量)
如果我们选择第i件物品,放在天平的左边,那么dp[i][j]就是从dp[ i -1 ] [ j - a [ i ] ]来的(将第i件物品选择前,质量差为j-a[i])转移过来
同理:如果放在天平的右边,那么dp[i][j]就是dp[i-1][j+a[i]]转移而来的
如果不放,那就是dp[i-1][j]直接转移过来
我们要找到最大的情况,就是上面三个取最大值
dp[i][j] = max(dp [ i -1 ] [ j ] , d p [ i - 1 ][ j - a [ i ] ] + a [ i ] ,d p [ i - 1 ] [j +a [ i] ] + a [ i ] )
因为题目有限定这个质量差的上限,所以最后我们求出这个范围内的最大质量即可
max(sum,dp[n][i])
#include<bits/stdc++.h>
#define endl '\n'
#define mk make_pair
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5+7;
const int mod=998244353;
const int inf=LLONG_MAX;
using i128=__int128;
int dp[105][11000];
void sovle(){
memset(dp,-0x3f,sizeof(dp));
int n,m;
cin>>n>>m;
vector<int>a(n+1);
int sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
}
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=sum;j++){
dp[i][j]=max(dp[i-1][j],max(dp[i-1][abs(j-a[i])]+a[i],dp[i-1][j+a[i]]+a[i]));
//初始化为负无穷,因为初始状态f[i][j]的值不一定为0,而应该是一个不存在的值.所以应该是负无穷
}
}
int max1=0;
for(int i=0;i<=m;i++){
max1=max(dp[n][i],max1);
}
cout<<max1<<endl;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0),cout.tie(0);
int t = 1;
//cin>>t;
while (t--){
sovle();
}
return 0;
}