题外话

由于本蒟蒻的动态规划实在是太弱啦,所以有必要爆破一下洛谷提高试炼场。里面有很多非常好,难度也合适的动态规划题……(然而你还是抄了不少题解
niconiconi~让我们一起开始爆破吧。

lv-1

P1005 矩阵取数游戏

难度评分:※※※
题目分析:
如果你做过洛谷P2858的话,相信这道题的状态转移方程还是很吼写的。首先把每一行分开考虑,f(i,j)表示取完第i到第j个数的最大得分,那么:

f(i,j)=max(f(i+1,j)+ai∗2m+i−j,f(i,j−1)+aj∗2m+i−j)

f(i,j)=max(f(i+1,j)+a_i*2^{m+i-j},f(i,j-1)+a_j*2^{m+i-j})
此题难点不在于动态规划,在于高精度!
O(≧口≦)O高精度真的好难打啊。本蒟蒻使用了重载运算符,这样编码难度会低一些,一定注意结构体中数组不要开得太大,使用压位后当然可以开小一点免得TLE。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define ri register int
const LL mod=1e12;//压位!
struct node{int l;LL t[205];}f[82][82],ans,a[82],bin[82];
int n,m;
void print(node tmp) {//输出printf("%lld",tmp.t[tmp.l]);for(ri i=tmp.l-1;i>=1;--i) printf("%012lld",tmp.t[i]);putchar('\n');
}
node operator + (node a,node b) {//加法node c;c.l=min(a.l,b.l);LL x=0;for(ri i=1;i<=a.l&&i<=b.l;++i) {c.t[i]=a.t[i]+b.t[i]+x;x=c.t[i]/mod,c.t[i]%=mod;}while(c.l<a.l) c.t[++c.l]=a.t[c.l]+x,x=c.t[c.l]/mod,c.t[c.l]%=mod;while(c.l<b.l) c.t[++c.l]=b.t[c.l]+x,x=c.t[c.l]/mod,c.t[c.l]%=mod;while(x) c.t[++c.l]=x,x=c.t[c.l]/mod,c.t[c.l]%=mod;return c;
}
node operator * (node a,node b) {//乘法node c;c.l=a.l+b.l;LL x=0;for(ri i=1;i<=c.l;++i) c.t[i]=0;for(ri i=1;i<=a.l;++i) {x=0;for(ri j=1;j<=b.l;++j) {c.t[i+j-1]+=a.t[i]*b.t[j]+x;x=c.t[i+j-1]/mod,c.t[i+j-1]%=mod;}c.t[i+b.l]+=x;}while(!c.t[c.l]&&c.l>1) --c.l;return c;
}
bool operator < (node a,node b) {//比较函数if(a.l>b.l) return 0;if(a.l<b.l) return 1;for(ri i=a.l;i>=1;--i)if(a.t[i]>b.t[i]) return 0;else if(a.t[i]<b.t[i]) return 1;return 1;
}
void init() {//打表2的次方bin[0].l=1,bin[0].t[1]=1;node x;x.l=1,x.t[1]=2;for(ri i=1;i<=m;++i) bin[i]=bin[i-1]*x;
}
int main()
{scanf("%d%d",&n,&m);ans.l=1;init();while(n--) {for(ri j=1;j<=m;++j)a[j].l=1,scanf("%lld",&a[j].t[1]),f[j][j]=a[j]*bin[m];for(ri i=m;i>=1;--i)//在重载运算符后,直接dp即可for(ri j=i+1;j<=m;++j) {f[i][j]=max(f[i+1][j]+a[i]*bin[m+i-j],f[i][j-1]+a[j]*bin[m+i-j]);}ans=ans+f[1][m];}print(ans);return 0;
}

P1373 小a和uim之大逃离

难度评分:※※※
题目分析&代码:
本蒟蒻的另一篇博客

P2279 消防局的设立

难度评分:※※
题目分析:
这题……不是贪心吗?
考虑深度最大的叶子节点,它可以被它的兄弟,它自己,它的父亲或是爷爷控制。可以发现,它的爷爷可以控制它自己,它的父亲和它的兄弟,所以在爷爷处建立消防局最优。
按深度从大到小处理,对于每一个没有被控制的点,在它爷爷处建立消防局。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
int read(){int w=1,q=0;char ch=' ';while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();if(ch=='-')w=-1,ch=getchar();while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();return w*q;
}
int n,tot,dee,ans;
vector<int>cen[1005];
int ne[2005],h[1005],go[2005],fa[1005];
bool vis[1005];
void add(int x,int y){go[++tot]=y;ne[tot]=h[x];h[x]=tot;}
void dfs(int x,int las,int dep){dee=max(dee,dep);fa[x]=las;cen[dep].push_back(x);for(int i=h[x];i;i=ne[i])if(go[i]!=las)dfs(go[i],x,dep+1);
}
void bfs(int x,int bs,int las){vis[x]=1;if(bs==2)return;for(int i=h[x];i;i=ne[i])bfs(go[i],bs+1,x);
}
int main()
{int i,j,x;n=read();for(i=2;i<=n;i++){j=read();add(i,j);add(j,i);}dfs(1,0,1);for(i=dee;i>=1;i--)for(j=0;j<cen[i].size();j++){x=cen[i][j];if(vis[x])continue;ans++;if(fa[fa[x]])bfs(fa[fa[x]],0,0);else if(fa[x])bfs(fa[x],0,0);else bfs(x,0,0);}printf("%d",ans);return 0;
} 

P1220 关路灯

难度评分:※※※※
题目分析:
啊呀洛谷的数据范围开小了啦,搞得暴力都能过。这其实应该是一个有关未来费用的区间dp问题。
如果大家对这一类问题感兴趣的花,还可以看看:修长城,送披萨
由于先往左边走一段关了一盏灯再跑到右边关一盏灯什么的很蠢,所以关的灯一定是一段连续的区间,并且关完灯后处于这个区间的左边或右边。所以我们用f(i,j,0)表示关完[i,j]区间后处于左边,f(i,j,1)表示关完[i,j]区间后处于右边,我们在处理一段区间的时候同时算上所有灯因为没有关灯而花费的费用。
我们用w(i,j)表示除了[i,j]以外的灯功率和(用前缀和维护或预处理)
f(i,j,0)=f(i,j,0)=
min(f(i+1,j,0)+w(i+1,j)∗(di+1−di),f(i+1,j,1)+w(i+1,j)∗(dj−di))min(f(i+1,j,0)+w(i+1,j)*(d_{i+1}-d_i),f(i+1,j,1)+w(i+1,j)*(d_j-d_i))
f(i,j,1)=f(i,j,1)=
min(f(i,j−1,0)+w(i,j−1)∗(dj−di),f(i,j−1,1)+w(i,j−1)∗(dj−dj−1))min(f(i,j-1,0)+w(i,j-1)*(d_j-d_i),f(i,j-1,1)+w(i,j-1)*(d_j-d_{j-1}))
代码:
其实此题比较推荐记忆化搜索

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<vector>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
int n,m;
long long d[1005],w[1005],r[1005][1005];
long long f[1005][1005][2],ans;
int main()
{int i,j;long long x;scanf("%d%d",&n,&m);for(i=1;i<=n;i++){scanf("%lld%lld",&d[i],&x);w[i]=w[i-1]+x;}for(i=1;i<=n;i++)for(j=1;j<=n;j++)r[i][j]=w[i-1]+w[n]-w[j];memset(f,127/3,sizeof(f));f[m][m][1]=f[m][m][0]=0;for(i=n-1;i>=1;i--)for(j=i+1;j<=n;j++){f[i][j][0]=min((long long)f[i+1][j][0]+r[i+1][j]*(d[i+1]-d[i]),(long long)f[i+1][j][1]+r[i+1][j]*(d[j]-d[i]));f[i][j][1]=min((long long)f[i][j-1][0]+r[i][j-1]*(d[j]-d[i]),(long long)f[i][j-1][1]+r[i][j-1]*(d[j]-d[j-1]));}ans=min(f[1][n][1],f[1][n][0]);printf("%lld",ans);return 0;
}

lv-2

P1040 加分二叉树

难度评分:※
题目分析:
用f(i,j)表示处理序列中[i,j]这一段的最大加分,由于中序遍历一定是(左子树)(根)(右子树),所以我们枚举这一段子树中根在哪儿,实现状态转移,具体方程为:

f(i,j)=max(f(i,k−1)∗f(k+1,j)+ak)

f(i,j)=max(f(i,k-1)*f(k+1,j)+a_k)
a是加分。注意一下边界情况。
代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<climits>
using namespace std;
int n;
int a[31],fa[31][31];
long long f[31][31];
void print(int now,int l,int r){printf("%d ",now);if(now-1>=l)print(fa[l][now-1],l,now-1);if(now+1<=r)print(fa[now+1][r],now+1,r);
}
int main()
{int i,j,k;long long ans=0;scanf("%d",&n);for(i=1;i<=n;i++) { scanf("%d",&a[i]);f[i][i]=a[i];fa[i][i]=i;}for(i=n-1;i>=1;i--){for(j=i+1;j<=n;j++){if(f[i][j]<f[i+1][j]+a[i])f[i][j]=f[i+1][j]+a[i],fa[i][j]=i;if(f[i][j]<f[i][j-1]+a[j])f[i][j]=f[i][j-1]+a[j],fa[i][j]=j;for(k=i+1;k<=j-1;k++){ans=f[i][k-1]*f[k+1][j]+a[k];if(f[i][j]<ans){f[i][j]=ans;fa[i][j]=k;}}}}printf("%lld\n",f[1][n]);print(fa[1][n],1,n);return 0;
}

P1273 有线电视网

难度评分:※※
题目分析:
树上背包问题,几乎是裸题。
但是数据加强后好像过不去了desi……
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
int read(){int w=1,q=0;char ch=' ';while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();if(ch=='-')w=-1,ch=getchar();while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();return w*q;
}
int n,m,tot;
int ne[6005],h[3005],go[6005],coss[6005],mon[3005];
int f[3005][3005];
void add(int x,int y,int z){go[++tot]=y;coss[tot]=z;ne[tot]=h[x];h[x]=tot;
}
int dfs(int x){int i,j,k,kl=0;if(x>n-m){f[x][1]=mon[x];return 1;}for(i=h[x];i;i=ne[i])kl+=dfs(go[i]);for(i=h[x];i;i=ne[i])for(j=kl;j>=1;j--){for(k=1;k<=j;k++)f[x][j]=max(f[x][j],f[x][j-k]+f[go[i]][k]-coss[i]);}return kl;
}
int main()
{int i,j,num,x,y;n=read();m=read();for(i=1;i<=n;i++)for(j=1;j<=m;j++)f[i][j]=-99999999;for(i=1;i<=n-m;i++){num=read();for(j=1;j<=num;j++){x=read();y=read();add(i,x,y);}}for(i=n-m+1;i<=n;i++)mon[i]=read();x=dfs(1);for(i=m;i>=0;i--)if(f[1][i]>=0){printf("%d",i);return 0;}return 0;
} 

P1156 垃圾陷阱

难度评分:※
题目分析:
令f(i,j)表示垃圾堆了i高度时卡门还可以活j小时是否可以实现。
然后就有堆放垃圾和吃垃圾两种操作,转移还是很easy的吧。
代码:
两年前的老代码了……风格清奇……两格缩进,超多头文件,超多大括号嵌套,cin和cout读入输出,手打冒泡排序……

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<cstdlib>
#include<climits>
using namespace std;
bool f[400][6000]={0};
struct lj{int time;int h;int e;
}lj[101];
int main()
{int n,m=10,d,k,i,j;cin>>d>>n;for(i=1;i<=n;i++){cin>>lj[i].time>>lj[i].e>>lj[i].h;m+=lj[i].e;}for(i=1;i<=n-1;i++)//两年前的我手打的冒泡排序QAQfor(j=1;j<=n-i;j++){if(lj[j+1].time<lj[j].time){lj[0]=lj[j];lj[j]=lj[j+1];lj[j+1]=lj[0];}}f[0][10]=1;for(i=1;i<=n;i++){for(k=d;k>=0;k--)for(j=m;j>=lj[i].time;j--){if(f[k][j]==1){ if(k+lj[i].h>=d){ cout<<lj[i].time;return 0;}f[k+lj[i].h][j]=1;f[k][j+lj[i].e]=1;}}}i=m;while(f[0][i]==0)i--;cout<<i;return 0;
}

P1169 棋盘制作

难度评分:※※※※
题目分析&代码:戳我n(≧▽≦)n

P2467 地精部落

难度评分:※※※※
题目分析:
感谢aiyougege在洛谷发布的题解。
对于一个排列,我们只关心每一个数的相对大小。所以我们可以令f(i,j)表示一个i个元素的排列,第一个数是上升的,并且第i个数在这i个数中的相对大小为j时的方案数。
现在我们需要几个结论:
1.在n->n-1的转化过程中,我们删除了一个点后,我们可以将n-1个点视为仍是1~n-1的排列。
2.在若排列Pn为一个合法抖动子序列,则交换i∈[1,n)与i+1,必能得到另一个抖动子序列。
3.抖动序列的对称性,若存在第一段上升的长度为n的抖动子序列,则以n+1-x代x必能得到一个第一段下降的长度为n的抖动子序列。
都很显然。
所以最后答案是f(n,n)*2
转移方程呢?我们考虑j与j-1是否相邻。
如果不相邻:将j-1与j交换显然排列个数不变,这种情况有f(i,j-1)种情况
如果相邻:那么我们需要一个i-1个元素的最后一个元素是j-1,第一个元素是下降的序列方案数,这样第j-1个元素在i-1个元素中的相对大小应该为j-1,又因为要求下降序列,根据结论3,所以可以得到方案数为f(i-1,i-j+1)
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
int n;LL m,ans,f[2][4205];
int main()
{scanf("%d%lld",&n,&m);f[1][1]=1;for(int i=2;i<=n;++i) {int t=i&1;for(int j=1;j<=i;++j)f[t][j]=(f[t][j-1]+f[t^1][i-j+1])%m;}for(int i=1;i<=n;++i) ans=(ans+f[n&1][i])%m;printf("%lld",(ans+ans)%m);return 0;
}

lv-3

P1415 拆分数列

难度评分:※※※※
题目分析:
两次dp,一次d(i)表示数列中第i个数为结尾时,最后一个数最小,最后一个数开头的位置,dp方法见代码work1。
一次f(i)表示数列中第i个数为开头时,第一个数最大,第一个数结尾的位置,dp方法见work2。不过单纯地dp是不行的,因为对于最后一个数有前导0的情况,d(i)并不会处理,所以应该在work2中做处理。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=505;
int n,a[N],f[N],d[N];
char s[N];
int bj(int l1,int r1,int l2,int r2) {//比较大小的函数if(!r1) return 1;while(!a[l1]&&l1<r1) ++l1;while(!a[l2]&&l2<r2) ++l2;if(r1-l1+1>r2-l2+1) return 0;if(r1-l1+1<r2-l2+1) return 1;for(int i=0;i<=r1-l1;++i)if(a[l1+i]>a[l2+i]) return 0;else if(a[l1+i]<a[l2+i]) return 1;return 0;
}
void work1() {for(int i=1;i<=n;++i)for(int j=i;j>=1;--j)if(bj(d[j-1],j-1,j,i)){d[i]=j;break;}
}
void work2() {f[d[n]]=n;int zero=d[n];while(zero>1&&!a[zero-1]) f[zero-1]=n,--zero;//看一看最后一个数的前导0for(int i=zero-1;i>=1;--i)for(int j=d[n]-1;j>=i;--j)if(bj(i,j,j+1,f[j+1])){f[i]=j;break;}
}
int main()
{scanf("%s",s);n=strlen(s);for(int i=1;i<=n;++i) a[i]=s[i-1]-'0';work1();work2();for(int x=1,y;x<=n;x=y) {y=x;while(y<=f[x]) printf("%d",a[y]),++y;if(y<=n) putchar(',');}return 0;
}

P2051 中国象棋

难度评分:※※※※※
题目分析:
用f(i,x1,x2)表示考虑了前i行,有x1列放了一个棋子,有x2列放了两个棋子。
然后状态转移方程,好像,难打。
第i行不放棋子:

f(i−1,x1,x2)

f(i-1,x1,x2)
放一颗棋子,可以放在一个没放棋子的列上,或放在一个放了一颗棋子的列上:

f(i−1,x1−1,x2)∗(m−x1−x2+1)+f(i−1,x1+1,x2−1)∗(x1+1)

f(i-1,x1-1,x2)*(m-x1-x2+1)+f(i-1,x1+1,x2-1)*(x1+1)
放两颗棋子,可以一个放没棋子的列,一个放有棋子的列。或者都放没棋子的列,或者都放有一颗棋子的列,再用组合数搞一搞:

f(i−1,x1,x2−1)∗(m−x1−x2+1)∗x1+

f(i-1,x1,x2-1)*(m-x1-x2+1)*x1+

f(i−1,x1−2,x2)∗(m−x1−x2+2)∗(m−x1−x2+1)2

f(i-1,x1-2,x2)*\frac{(m-x1-x2+2)*(m-x1-x2+1)}2

f(i−1,x1+2,x2−2)∗(x1+2)∗(x1+1)2

f(i-1,x1+2,x2-2)*\frac{(x1+2)*(x1+1)}2
代码:
调了三个小时QAQ

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
LL mod=9999973;
int n,m;LL f[105][105][105],ans;
LL qm(LL x){return x>=mod?x-mod:x;}
int main()
{scanf("%d%d",&n,&m);f[0][0][0]=1;for(int i=1;i<=n;++i)for(int x1=0;x1<=m;++x1)for(int x2=0;x2+x1<=m;++x2) {f[i][x1][x2]=f[i-1][x1][x2];if(x1) f[i][x1][x2]=qm(f[i][x1][x2]+f[i-1][x1-1][x2]*(m-x1-x2+1)%mod);if(x2) f[i][x1][x2]=qm(f[i][x1][x2]+f[i-1][x1+1][x2-1]*(x1+1)%mod);if(x1>=2) f[i][x1][x2]=qm(f[i][x1][x2]+(LL)(m-x1-x2+2)*(m-x1-x2+1)/2*f[i-1][x1-2][x2]%mod);if(x2) f[i][x1][x2]=qm(f[i][x1][x2]+f[i-1][x1][x2-1]*(m-x1-x2+1)*x1%mod);if(x2>=2) f[i][x1][x2]=qm(f[i][x1][x2]+(LL)(x1+2)*(x1+1)/2*f[i-1][x1+2][x2-2]%mod);if(i==n) ans=qm(ans+f[i][x1][x2]);}printf("%lld",ans);return 0;
}

P2157 学校食堂

难度评分:※※※※※
题目分析:
状态压缩dp,用f(i,zt,j)表示当前处理到第i个人,第i个人及其后面7个人是否打饭的情况,上一个打饭的人是第i+j个人的时候的最优解。−8≤j≤7-8\leq j \leq 7,所以在使用数组储存的时候要加8.
然后枚举下一个打饭的人(必须合法)即可实现状态转移,在zt中表示第i个人已经打了饭的时候,要转移至f(i+1,zt/2,j)
具体方程见代码喵。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ri register int
const int N=1005,inf=1061109567;
int n,T,ans;
int w[N],b[N],bin[10],f[N][260][16];
void chk(int &x,int y){x=(y<x?y:x);}
int main()
{scanf("%d",&T);bin[0]=1;for(ri i=1;i<=8;++i) bin[i]=bin[i-1]<<1;while(T--) {scanf("%d",&n);for(ri i=1;i<=n;++i) scanf("%d%d",&w[i],&b[i]);memset(f,0x3f,sizeof(f));f[1][0][7]=0,ans=inf;for(ri i=1;i<=n;++i)for(ri zt=0;zt<bin[8];++zt)for(ri j=0;j<=15;++j) {if(f[i][zt][j]==inf) continue;if(zt&bin[0]) {chk(f[i+1][zt>>1][j-1],f[i][zt][j]);}//实现i系列向i+1系列的转移else {int lim=inf;for(ri k=0;k<=b[i]&&k+i<=lim;++k) {if(zt&bin[k]) continue;if(i+k>n) continue;lim=min(lim,b[k+i]+k+i);//记录最多到谁还可以打饭int t1=w[i+(j-8)],t2=w[i+k],c=(i+(j-8)==0?0:(t1|t2)-(t1&t2));chk(f[i][zt|bin[k]][k+8],f[i][zt][j]+c);//状态转移}}}for(ri i=0;i<=8;++i) ans=min(ans,f[n+1][0][i]);printf("%d\n",ans);}return 0;
}

P2216 理想的正方形

难度评分:※※
题目分析:
这题真的是动态规划吗?
不是几乎裸的滑动窗口类单调队列题吗?
对每一行维护两个单调队列,记录该行某一列为右端,长为n的滑动窗口中的最大值和最小值。
然后对每一列维护两个单调队列,记录该列某一行为下端,长为n的滑动窗口,上一次维护结果的最大值和最小值即可得到答案。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1505;
int a,b,n,ans=2e9+8;
int w[N][N],mi[N][N],mx[N][N],qmi[N],qmx[N];
int main()
{scanf("%d%d%d",&a,&b,&n);for(int i=1;i<=a;++i)for(int j=1;j<=b;++j) scanf("%d",&w[i][j]);for(int i=1;i<=a;++i) {int hmi=1,hmx=1,tmi=1,tmx=1;for(int j=1;j<=b;++j) {while(hmi<tmi&&w[i][qmi[tmi-1]]>=w[i][j]) --tmi;qmi[tmi++]=j;while(hmx<tmx&&w[i][qmx[tmx-1]]<=w[i][j]) --tmx;qmx[tmx++]=j; if(j<n) continue;while(hmi<tmi&&qmi[hmi]<=j-n) ++hmi;mi[i][j]=w[i][qmi[hmi]];while(hmx<tmx&&qmx[hmx]<=j-n) ++hmx;mx[i][j]=w[i][qmx[hmx]];}}for(int j=n;j<=b;++j) {int hmi=1,hmx=1,tmi=1,tmx=1;for(int i=1;i<=a;++i) {while(hmi<tmi&&mi[qmi[tmi-1]][j]>=mi[i][j]) --tmi;qmi[tmi++]=i;while(hmx<tmx&&mx[qmx[tmx-1]][j]<=mx[i][j]) --tmx;qmx[tmx++]=i; if(i<n) continue;while(hmi<tmi&&qmi[hmi]<=i-n) ++hmi;while(hmx<tmx&&qmx[hmx]<=i-n) ++hmx;ans=min(ans,mx[qmx[hmx]][j]-mi[qmi[hmi]][j]);}}printf("%d",ans);return 0;
}

P2331 最大子矩阵

难度评分:※※※※※
题目分析:
讲完了有两列的情况,只有一列的情况肯定会了。
f(zt,i,j)表示当前行的选择状态为zt,前i行选择j个子矩阵的最优解。
zt表示的含义:
0:不选该行,1:选择第一列,2:选择第二列,3:第一列和第二列都选择,且在同一个子矩阵中,4:第一列和第二列都选择,且分别位于两个子矩阵中。
然后转移……大概有25条转移语句,看代码吧,真不想再写一遍了。
代码:
调了一晚上,一度崩溃

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,num,ans,kl;
int f[5][105][11],a[105][3];
//0:空出,1:第一列,2:第二列,3:都选(一起),4:都选(不一起)
int main()
{scanf("%d%d%d",&n,&m,&num);for(int i=1;i<=n;++i)for(int j=1;j<=m;++j) scanf("%d",&a[i][j]);if(m==1) {for(int i=1;i<=n;++i)for(int k=1;k<=num;++k) {f[0][i][k]=max(f[0][i-1][k],f[1][i-1][k]);f[1][i][k]=max(f[1][i-1][k],f[0][i-1][k-1])+a[i][1];}printf("%d",max(f[1][n][num],f[0][n][num]));}else {memset(f,-0x3f,sizeof(f));for(int i=0;i<=n;++i)for(int j=0;j<=num;++j) f[0][i][j]=0;for(int i=1;i<=n;++i)for(int k=1;k<=num;++k) {f[0][i][k]=max(f[0][i-1][k],max(f[1][i-1][k],f[2][i-1][k]));f[0][i][k]=max(f[0][i][k],max(f[3][i-1][k],f[4][i-1][k]));f[1][i][k]=max(max(f[0][i-1][k-1],f[1][i-1][k]),f[2][i-1][k-1]);f[1][i][k]=max(max(f[3][i-1][k-1],f[4][i-1][k]),f[1][i][k])+a[i][1];f[2][i][k]=max(max(f[0][i-1][k-1],f[1][i-1][k-1]),f[2][i-1][k]);f[2][i][k]=max(max(f[3][i-1][k-1],f[4][i-1][k]),f[2][i][k])+a[i][2];f[3][i][k]=max(max(f[0][i-1][k-1],f[1][i-1][k-1]),f[2][i-1][k-1]);f[3][i][k]=max(max(f[3][i-1][k],f[4][i-1][k-1]),f[3][i][k])+a[i][1]+a[i][2];f[4][i][k]=max(max(f[1][i-1][k-1],f[2][i-1][k-1]),f[4][i-1][k]);if(k>=2) f[4][i][k]=max(max(f[3][i-1][k-2],f[0][i-1][k-2]),f[4][i][k]);f[4][i][k]+=a[i][1]+a[i][2];}ans=max(f[0][n][num],max(f[1][n][num],f[2][n][num]));ans=max(ans,max(f[3][n][num],f[4][n][num]));printf("%d",ans);}return 0;
}

蒟蒻努力中。。。

本蒟蒻爆破了试炼场后,并改不了自己还是很蒻的事实,总之,会继续fighting的!

对于洛谷提高试炼场-动态规划篇的爆破相关推荐

  1. 洛谷P1130 红牌 动态规划

    洛谷P1130 红牌 动态规划 状态转移方程   dp[ j ][ i ] = dp[ j-1 ][ i-1 ] + dp[ j ][ i-1 ]   然后 1 的时候判一下就行 1 #include ...

  2. 【洛谷】【动态规划/二维背包】P1855 榨取kkksc03

    [题目描述:] ... (宣传luogu2的内容被自动省略) 洛谷的运营组决定,如果...,那么他可以浪费掉kkksc03的一些时间的同时消耗掉kkksc03的一些金钱以满足自己的一个愿望. Kkks ...

  3. 【洛谷】【动态规划+单调队列】P1725 琪露诺

    [题目描述:] 在幻想乡,琪露诺是以笨蛋闻名的冰之妖精. 某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来.但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸.于是琪露诺决定到河 ...

  4. 洛谷省选斗兽场全通关祭~以及之后的打算!

    18/5/2:洛谷省选斗兽场全通关祭! 洛谷也算是坚持了下去,到现在为止已经是有244道题AC了. 虽然我各大oj的刷题量加在一起也没有大佬的十分之一,我的智商也不及大佬的百分之一,但是相信智商刷也是 ...

  5. 洛谷-题解 P2672 【推销员】

    独门思路!链表加优先队列! 这题一望,贪心是跑不掉了,但是我贪心并不好,所以想到了一个复杂一些但思路更保稳的做法 思路: 1 因为是离线操作,所以我们可以倒着求,先求x=n的情况,因为那样直接就知道了 ...

  6. 闲来无事刷水题、简单博弈论专题、sg函数、洛谷

    记 今天闲来无事,不想刷codeforces了,到洛谷提高组训练营找几道水题刷着玩玩(虽然自己早已过了打OI的年纪)- 简单博弈论专题 P1199 三国游戏 这么考虑,由于电脑总是不能让我搭配出当前能 ...

  7. 洛谷题单 算法1-3 暴力枚举

    1 First Step (ファーストステップ) 题目背景 知らないことばかりなにもかもが(どうしたらいいの?) 一切的一切 尽是充满了未知数(该如何是好) それでも期待で足が軽いよ(ジャンプだ!) ...

  8. 洛谷P1020:导弹拦截

    P1020 [NOIP1999 普及组] 导弹拦截 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 这道题其实是两个问题的结合,可以互不干扰地求出. 第一个问题,NOPI里是可以用o ...

  9. 洛谷 - 试炼场(全部题目备份)

    整理的算法模板合集: ACM模板 目录 1.新手村 1 - 1 洛谷的第一个任务 1 - 2 顺序与分支 1 - 3 循环!循环!循环! 1 - 4 数组 1 - 5 简单字符串 1 - 6 过程函数 ...

最新文章

  1. TensorFlow练习27: 验证码生成器-从文本生成图像
  2. 同软件多个线程设置不同ip_多线程--面试知识
  3. 可持久化线段树学习笔记
  4. 哪些蔬菜基本不会使用农药?
  5. (12.05)Java小知识!
  6. matlab怎么计算行列式,matlab一元线性回归方程的计算和检验/用四种方法计算行列式...
  7. VSCode下载安装和修改插件下载位置(配置右键菜单)
  8. VS code Could not establish connection to IP 解决方法
  9. [CF235C] Cyclical Quest
  10. vue页面的定时刷新
  11. Spring Data JPA 原理与实战第二天 掌握Repoitory和DQM
  12. 基于51单片机的多路多点温度检测两种供电方式proteus仿真原理图PCB
  13. Fragment在ViewPager中的生命周期
  14. choose ,when ,otherwise
  15. USACO-Cave Cows 2
  16. 使用R做方差分析实现多重比较可视化结果
  17. java的框架是干嘛的_spring框架是干什么的
  18. 哈希表及哈希冲突的解决
  19. pygame.error: mpg123_seek: Invalid RVA mode问题解决
  20. D - Vessels(堆优化模拟)

热门文章

  1. 热修复原理学习(1)热修复技术介绍
  2. 软件开发中的王者荣耀理论
  3. 互联网日报 | 4月8日 星期四 | 蔚来第10万辆量产车下线;哈啰进入电单车生产销售领域;携程宣布正式开始港股招股...
  4. DeepLearning4j-使用Java训练YOLO模型
  5. 笔试题:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。该题有三种解法:递归的方法求解斐波那契数列、用概率与统计的数学方法解决,3.动态规划
  6. 2008系统更改计算机名,用Windows server 2008 R2更改计算机名的方法
  7. 手机端酒店机票预订页面HTMLcss3+html5模板
  8. 文件在另一个程序中打开,无法删除~【删除文件被占用问题】(保姆级教程,五种解决办法~)
  9. 基于机器学习与深度学习的金融风控贷款违约预测
  10. 取模 乘法和除法运算在CPU和GPU上的效率