九十年代ioi压轴题,零零年代noi压轴题,如今的普及组签到题
多少朝代更迭,古今辛酸
皆付红尘,巷陌笑语中
回归正题
对于该题,我比较喜欢递推做法,因为这样可以明确地将动态规划与贪心分开
递推有两种
第一种是由前推当前
第二种是由当前推后
具体选择视题目而定
比如这道题就要选第一种
一个数更新到下面两个数不能取max,因为这种贪心显然不对
然而若两个数同时更新一个数取max则是最优
等等这不还是贪心嘛
#include
#include
using namespace std;
int const maxn=1101;
int f[maxn][maxn],map[maxn][maxn],ans;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
scanf("%d",&map[i][j]);
f[1][1]=map[1][1];
//dp题初始化是关键
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
f[i][j]=max(f[i-1][j],f[i-1][j-1])+map[i][j];
for(int i=1;i<=n;i++)
ans=max(ans,f[n][i]);
printf("%d\n",ans);
return 0;
}
该题思想上没什么难度,但是细节较多
还是上一题的递推思想,由前面的值更新当前的
#include
#include
typedef long long ll;
using namespace std;
ll f[30][30];
int vis[30][30];
int main()
{
ll m,n,x,y;
scanf("%lld%lld%lld%lld",&n,&m,&x,&y);
n+=1,m+=1,x+=1,y+=1;
//下标搞出负数会越界,我不想基佬紫,于是我就把棋盘平移了一下
int dx[9]={0,2,2,-2,-2,1,1,-1,-1};
int dy[9]={0,1,-1,1,-1,2,-2,2,-2};
//模拟马走日的过程
for(int i=0;i<=9;i++)
vis[x+dx[i]][y+dy[i]]=true;
f[0][1]=1;
//为什么不初始化起点呢
//显然是由于先初始化再状态转移会导致初始值被覆盖
//想要初始化原点也行,转移的时候特判一下就完事了
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(!vis[i][j])
f[i][j]=f[i][j-1]+f[i-1][j];
printf("%lld\n",f[n][m]);
return 0;
}
该题利用记忆化
每当找到一个数子序列的尾部,就扫一遍这个数的前面来更新最优子结构
#include
#include
typedef long long ll;
using namespace std;
ll f[5100],a[5100],ans;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
f[i]=1;
//初始化,每个数都是一个以该数结尾长度为1的子序列
}
for(int i=2;i<=n;i++)
//i从2开始,养成好习惯
{
for(int j=1;j//j无法取到i,好习惯
if(a[j]1);
if(f[i]>ans)
ans=f[i];
}
printf("%lld",ans);
return 0;
}
这个题不能臆想要手模分析
假如河这边只剩一个人,手电肯定在对面
此时最优解是跑得最快的送手电过来,再一起过去
假如河这边有两个人,手电在对面
最优解是跑的最快的送手电过来,这边的两个人一起过去,次快的过来,一起回去
如果是多个人都可以分解成上述两种
因为符合最优子结构,所以上述两种要取更优
设f[i]为i个人过河的最小渡河时间,a[]为排序后的数组
转移如下
f[i]=min(f[i-1]+a[1]+a[i],f[i-2]+a[1]+a[i]+a[2]*2)
#include
#include
#include
using namespace std;
int const maxn=100011;
int a[maxn],f[maxn];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
f[1]=a[1];
f[2]=a[2];
//注意初始化
for(int i=3;i<=n;i++)
f[i]=min(f[i-1]+a[1]+a[i],f[i-2]+a[1]+a[i]+a[2]*2);
printf("%d",f[n]);
return 0;
}
这个题有个证明,就是
Dilworth定理:偏序集的最少反链划分数等于最长链的长度
然后一顿操作就过了
#include
#include
using namespace std;
int const maxn=100011;
int f[maxn],f1[maxn],a[maxn],ans,ans1,n;
int main()
{
while(scanf("%d",&a[++n])!=EOF)
{
f[n]=1;
f1[n]=1;
}
n--;
for(int i=2;i<=n;i++)
{
for(int j=1;jif(a[i]<=a[j])
f[i]=max(f[i],f[j]+1);
else
f1[i]=max(f1[i],f1[j]+1);
}
ans=max(ans,f[i]);
ans1=max(ans1,f1[i]);
}
printf("%d\n%d\n",ans,ans1);
return 0;
}
这题虽然水,但是挺有意思..
简单来说就是
找以i为尾最长上升子序列
反向找以i为尾的最长上升子序列(即找以i为首的最长下降)
然后枚举中间那个人i就行了
#include
#include
#include
using namespace std;
int f[111][3],a[111],b[111],n,ans;
bool cmp(int x,int y){
return a[x]>a[y];
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=i;
f[i][0]=1;
f[i][1]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
if(a[i]>a[j])
f[i][0]=max(f[i][0],f[j][0]+1);
for(int i=n;i>=1;i--)
for(int j=n;j>=i;j--)
if(a[i]>a[j])
f[i][1]=max(f[i][1],f[j][1]+1);
for(int i=1;i<=n;i++)
ans=max(ans,f[i][0]+f[i][1]-1);
cout<
满足一个条件,排个序
然后找另一个条件的最长子序列
#include
#include
#include
#include
using namespace std;
int const maxn=3000;
int f[maxn],ans,ans2,b[maxn];
struct node
{
int iq,e;
}a[maxn];
int cmp(int x,int y)
{
return a[x].iq<=a[y].iq;
//这里要注意<=和<的区别,详见博客
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].iq,&a[i].e);
f[i]=1;
b[i]=i;
}
sort(b+1,b+1+n,cmp);
//按iq排序
for(int i=2;i<=n;i++)
{
for(int j=1;jif(a[b[i]].e>=a[b[j]].e)
f[i]=max(f[j]+1,f[i]);
ans=max(ans,f[i]);
}
printf("%d",ans);
return 0;
}
#include
#include
int const maxn=10000001;
int const inf=-1*0x7f7f7f7f;
long long f[maxn],a[maxn],ans,f2[maxn],t[maxn],s[maxn];
int main()
{
int n,p,x;
long long maxx;
scanf("%d%d",&n,&p);
maxx=inf;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=std::max(s[i-1],s[0])+x;
maxx=std::max(s[i],maxx);
t[i]=maxx%p;
}
ans=f[1]=t[1];
maxx=inf;
for(int i=2;i<=n;i++)
{
maxx=std::max(maxx,f[i-1]+t[i-1]);
f[i]=maxx;
ans=std::max(ans,f[i])%p;
}
printf("%lld",ans);
return 0;
}
//分数不降
//因此i的分数=前一个人的特征值(即i~j的最大字段和)+前一个人的分数
//一道水题做了2天...说到底还是缺乏对求最大子段和的算法原理的了解