DP入门
AT-abc286-d
题意简述
有
请求出在不找零的情况下,用这些纸币能否正好付
题目解析
类似多重背包,设
循环枚举,
具体实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=55;
const int M=1e4+10;
int n,x;
int a[N],b[N];
int f[N][M];
int main(){
scanf("%d%d",&n,&x);
for(int i=1;i<=n;++i) scanf("%d%d",&a[i],&b[i]);
f[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=b[i];++j){
for(int k=0;k<=x;++k){
if(a[i]*j>k) continue;
if(f[i-1][k-a[i]*j]) f[i][k]=1;
}
}
}
if(f[n][x]) puts("Yes");
else puts("No");
return 0;
}
AT-dp-l
题意简述
有一个双端队列,双方轮流取数,只能从队头或队尾取数,取完数后将这个数从队列中弹出。双方都希望自己取的所有数之和尽量大,且双方都以最优策略行动,假设先手取的所有数之和为
题目解析
经典套路。
设
考虑子状态,
如果轮到
否则
问题来了,如何判断到谁取数?
发现取到
具体实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
const int N=3005;
int n;
ll f[N][N],a[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%lld",&a[i]);
}
for(int len=1;len<=n;++len){
for(int i=1;i<=n;++i){
int j=i+len-1;
if(i==j){
if((i-1+n-j)&1) f[i][j]=-a[i];
else f[i][j]=a[i];
}
if((i-1+n-j)&1) f[i][j]=min(f[i+1][j]-a[i],f[i][j-1]-a[j]);
else f[i][j]=max(f[i+1][j]+a[i],f[i][j-1]+a[j]);
}
}
printf("%lld\n",f[1][n]);
return 0;
}
AT-tdpc-game
题意简述
- 如果两座山都空了,游戏结束。
- 如果只有某一座山空了,取走另一座山上的最上面的物品。
- 如果两座山都没有空,选择任意一座山,并取走其最上面的物品。
假设两人都采取最优策略,请求出 Alice 能取得的物品的价值总和。
题目解析
与上一题类似。
设
转移,若轮到
否则
再考虑 当
if((i+j)%2==0)
f[i][j]=max((i==n+1)?0:(f[i+1][j]+a[i]),(j==m+1)?0:(f[i][j+1]+b[j]));
else
f[i][j]=min((i==n+1)?INF:f[i+1][j],(j==m+1)?INF:f[i][j+1]);
当然,也可以将两座山拼起来,山顶朝外,注意判断一山取完的情况即可。此处不再详细说明。
具体实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
const int INF=0x3f3f3f3f;
int n,m;
int a[N],b[N];
int f[N][N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=m;++i) scanf("%d",&b[i]);
for(int i=n+1;i>=1;--i){
for(int j=m+1;j>=1;--j){
if(i==n+1&&j==m+1) continue;
if((i+j)%2==0)
f[i][j]=max((i==n+1)?0:(f[i+1][j]+a[i]),(j==m+1)?0:(f[i][j+1]+b[j]));
else
f[i][j]=min((i==n+1)?INF:f[i+1][j],(j==m+1)?INF:f[i][j+1]);
}
}
printf("%d\n",f[1][1]);
return 0;
}
AT-dp-i
题意简述
扔完所有硬币,求正面朝上的硬币数比反面朝上的硬币数多的概率。
题目解析
也是一种经典DP模型(概率模型)。
设
可以推出转移方程:前
注意
具体实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3005;
int n;
double p[N];
double f[N][N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%lf",&p[i]);
f[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=i;++j){
if(j==0) f[i][j]=f[i-1][j]*(1-p[i]);
else f[i][j]=f[i-1][j-1]*p[i]+f[i-1][j]*(1-p[i]);
}
}
double sum=0;
for(int i=(n/2)+1;i<=n;++i) sum+=f[n][i];
printf("%.10lf\n",sum);
return 0;
}
AT-abc118-d
题意简述
有
题目解析
看到此题,第一反应是完全背包。容量是消耗火柴根数,价值是?
发现要让拼成数字最大,但凡有点数学知识得知位数越多数越大,故首先让位数变多。
设
位数相同,从前向后,前面的数越大越好,考虑递归从大到小枚举此位是否能填此数。
即判断
具体实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e4+10;
int n,m;
int id[10]={0,2,5,5,4,5,6,3,7,6};
int f[N],a[N];
bool cmp(int a,int b){return a>b;}
void out(int n){
for(int i=1;i<=m;++i){
if(id[a[i]]>n) continue;
if(f[n]==f[n-id[a[i]]]+1){
printf("%d",a[i]);
out(n-id[a[i]]);
break;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i) scanf("%d",&a[i]);
sort(a+1,a+1+m,cmp);
memset(f,-0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=m;++i){
for(int j=id[a[i]];j<=n;++j){
f[j]=max(f[j],f[j-id[a[i]]]+1);
}
}
out(n);
return 0;
}
AT-abc265-e
题意简述
你现在在一个二维平面,会进行
- 移动
:从当前点 移动到 ; - 移动
:从当前点 移动到 ; - 移动
:从当前点 移动到 。
同时在这个平面上有
问
题目解析
这题与 P1002过河卒 有点类似,都是给出一个点能到的其他点,有一些点不能走,求路径数。
但这题的难点是数据范围
3次方……诶?题目就是给了3种移动方案啊!(高兴地拍起肚皮)
思路就有了,多维DP,设
判断是否能经过可用哈希,我太懒用了
具体实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
const int p=1e9;
const int mod=998244353;
const int N=305;
#define ll long long
int n,m;
int a,b,c,d,e,f;
ll x,y;
map<ll,int> ma;
ll dp[N][N][N],ans;
inline ll check(int i,int j,int k){return (1ll*i*a+1ll*j*c+1ll*k*e)*p+(1ll*i*b+1ll*j*d+1ll*k*f);}
int main(){
scanf("%d%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&e,&f);
for(int i=1;i<=m;++i){
scanf("%lld%lld",&x,&y);
ma[x*p+y]=1;
}
dp[0][0][0]=1;
for(int i=0;i<=n;++i){
for(int j=0;j<=n-i;++j){
for(int k=0;k<=n-i-j;++k){
if(ma[check(i,j,k)]) continue;
if(i>0) dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k])%mod;
if(j>0) dp[i][j][k]=(dp[i][j][k]+dp[i][j-1][k])%mod;
if(k>0) dp[i][j][k]=(dp[i][j][k]+dp[i][j][k-1])%mod;
if(i+j+k==n) ans=(ans+dp[i][j][k])%mod;
}
}
}
printf("%lld\n",ans);
return 0;
}
~ 撒花qwq ~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】