2024-04-12周赛总结笔记

4.12周赛总结

总叙

难度$\color{orange}适中$,但是我发挥的不好,主要还是模版没记牢

T1 B3635 硬币问题

题目链接:这里

考点:

动态规划,dp;动态规划初步

难点:

容易与完全背包混淆

比赛时的自己的思路/想法

用完全背包一个个去凑,凑出n

是否AC,若没有,错在哪里了

NO!整道题都理解错了,根本不是背包问题,普通的DP就可以(我是傻子

正确思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll dp[1000001],a[3]={1,5,11};
int n;
signed main(){
cin>>n;
memset(dp,1145141919810,sizeof(dp));
dp[0]=0;//边界
for(int i=0;i<3;i++){
for(int j=a[i];j<=n;j++){
dp[j]=min(dp[j],dp[j-a[i]]+1);
}
}
cout<<dp[n];
return 0;
}

T2 P1510 精卫填海

题目链接:这里

考点:

模拟;动态规划,dp;背包

难点:

几乎没有……

比赛时的自己的思路/想法

用多重背包去取,直到体力值用光

是否AC,若没有,错在哪里了

NO!背包那一块出现了一点问题(好像是模版不太对,还是思路不对)

正确思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h>
using namespace std;
long long a[100005],b[100005],f[100005];
int main(){
int v,n,c;
cin>>v>>n>>c;
f[0]=0;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
}
for(int i=1;i<=n;i++){
for(int j=c-b[i];j>=0;j--){
f[j+b[i]]=max(f[j+b[i]],f[j]+a[i]);
}
}
for(int i=1;i<=c;i++){
if(f[i]>=v){
cout<<c-i;
return 0;
}
}
cout<<"Impossible";
return 0;
}

T3 P1833 樱花

题目链接:这里

考点:

动态规划,dp;背包

难点:

计算时间,其他几乎没有……

比赛时的自己的思路/想法

用多重背包去取,直到体力值用光

是否AC,若没有,错在哪里了

NO!最后几分钟写不完了……

正确思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n,m,w[6*N],v[6*N],s[6*N],len,dp[1005];
int main(){
int x1,x2,y1,y2;//x1:x2 y1:y2
scanf("%d:%d %d:%d %d",&x1,&x2,&y1,&y2,&n);
m=(y1-x1)*60+(y2-x2);
for(int i=1;i<=n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(c==0){
w[++len]=a;
v[len]=b;
s[len]=c;
}
for(int j=1;j<=c;j<<=1){
w[++len]=a*j;
v[len]=b*j;
s[len]=j;
c-=j;
}
if(c>0){
w[++len]=a*c;
v[len]=b*c;
s[len]=c;
}
}
for(int i=1;i<=len;i++){
if(s[i]==0){
for(int j=w[i];j<=m;j++){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
else{
for(int j=m;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
}
printf("%d",dp[m]);
return 0;
}

T4 P1759 通天之潜水

题目链接:这里

考点:

动态规划,dp

难点:

输出路径

比赛时的自己的思路/想法

先计算总共可以用多久,计算过程中用s[i][j]记录最后输出

是否AC,若没有,错在哪里了

NO!计算路径那里炸了……

正确思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<bits/stdc++.h>
using namespace std;
const int N=10005;
int m,v,n,a[N],b[N],c[N];
int dp[2005][2005],p[2005][2005][105];//p[][][]保存路径
void fg(int i,int j,int k){//保存路径函数
for(int l=1;l<=p[j-a[i]][k-b[i]][0];l++)
p[j][k][l]=p[j-a[i]][k-b[i]][l];
p[j][k][0]=p[j-a[i]][k-b[i]][0]+1;//更新路径长度
p[j][k][p[j][k][0]]=i; //记录编号
}
int main(){
scanf("%d%d%d",&m,&v,&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
for(int i=1;i<=n;i++){
for(int j=m;j>=a[i];j--){
for(int k=v;k>=b[i];k--){
if(dp[j-a[i]][k-b[i]]+c[i]>dp[j][k]){//比较选与不选的更优值
dp[j][k]=dp[j-a[i]][k-b[i]]+c[i];
fg(i,j,k);
}
else if(dp[j-a[i]][k-b[i]]+c[i]==dp[j][k]){//正好相等,比较
bool fl=1;
p[j-a[i]][k-b[i]][p[j-a[i]][k-b[i]][0]+1]=i;
for(int l=1;l<=min(p[j-a[i]][k-b[i]][0]+1,p[j][k][0]);l++){//取min,更小
if(p[j-a[i]][k-b[i]][l]<p[j][k][l]){//如果更小
fg(i,j,k);//继续找更小的
break;
}
if(p[j-a[i]][k-b[i]][l]>p[j][k][l]){//如果更大
fl=0;//记录,推出
break;
}
}
if(p[j-a[i]][k-b[i]][0]+1<p[j][k][0]&&fl){//再次比较确认是否有结果
fg(i,j,k);
}
}
}
}
}
printf("%d\n",dp[m][v]);
for(int i=1;i<=p[m][v][0];i++){
printf("%d ",p[m][v][i]);//输出
}
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll m,v,n,a[101],b[101],c[101],f[10001][10001];
string s[201][201];
int main()
{
cin>>m>>v>>n;
for(int i=1;i<=n;i++) cin>>a[i]>>b[i]>>c[i];
for(int i=1;i<=n;i++)
for(int j=m;j>=a[i];j--)
for(int k=v;k>=b[i];k--)
{
if(f[j][k]<f[j-a[i]][k-b[i]]+c[i]||(f[j][k]==f[j-a[i]][k-b[i]]+c[i]&&s[j][k]>s[j-a[i]][k-b[i]]+char(i)))//强制转字符
{
f[j][k]=f[j-a[i]][k-b[i]]+c[i];
s[j][k]=s[j-a[i]][k-b[i]]+char(i);
}
}
cout<<f[m][v]<<endl;
for(int i=0;i<s[m][v].size();i++)
cout<<int(s[m][v][i])<<" ";
return 0;
}

总结

要好好记录错题,多练习DP有关的题目