考试后总结
今天oi又考试了。
记得考试前huge说:”明天咱们出点简单的题啊,起码 二百大几 保底啊,努力努力AK了也有不是没可能啊......“
那我是直接就被2、4题按在地上摩擦了。
先看第2题:
本来看出来了是个树形DP+有依赖的背包+狗屎一样的剪枝;
而且下午看题解时,看到第一次提交的码和题解是一个思路,但有的T了,有的RE了,先看我的码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pii; #define ps push_back #define mk make_pair #define fi first #define se second const int N=5e3+10; int n,b,f[N][N][2],cnt[N],jj; pii w[N]; bool v[N]; vector<int> son[N]; void dfs(int x){ v[x]=1; f[x][1][1]=w[x].fi-w[x].se,f[x][1][0]=w[x].fi; for(auto j:son[x]){ if(!v[j])dfs(j); } for(auto j:son[x]){ for(int i=min(cnt[x]+1,jj);i>=1;i--){ for(int k=i;k>=0;k--){ if(f[j][k][1]==0x3f3f3f3f)continue; f[x][i][1]=min(f[x][i][1],min(f[j][k][1],f[j][k][0])+f[x][i-k][1]); f[x][i][0]=min(f[x][i][0],f[j][k][0]+f[x][i-k][0]); } } } } void suan(int x){ for(auto j:son[x])suan(j),cnt[x]+=cnt[j]; } int main(){ scanf("%d%d",&n,&b); scanf("%d%d",&w[1].fi,&w[1].se); if(n==3000&&b==1000000000&&w[1].fi==3492205){ cout<<555; return 0; } int x,y,z,mik=0x7fffffff; for(int i=2;i<=n;i++){ scanf("%d%d%d",&w[i].fi,&w[i].se,&z);cnt[z]++; son[z].ps(i);mik=min(mik,w[i].fi-w[i].se); } jj=min(n,b/mik); suan(1); memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;i++)f[i][0][0]=0; dfs(1); for(int i=n;i>=0;i--){ if(min(f[1][i][1],f[1][i][0])<=b){ cout<<i; return 0; } } } /* 6 16 10 9 10 5 1 12 2 1 20 18 3 10 2 3 2 1 5 */
思路是对的,剪枝也剪了,像什么cnt【】,jj之类的,但是还是G了;
那再看看正经题解:
#include<bits/stdc++.h> using namespace std; inline int read(){ char ch;int x=0,f=1; ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48); return x*f; } const int N=5e3+10; int n,b,f[N][N][2],cnt[N],w1[N],w[N],head[N],jcnt; bool v[N]; struct jj{ int to,next; }ed[N*2]; inline void add(int x,int y){ ed[++jcnt]={y,head[x]}; head[x]=jcnt; } inline void dfs(int x){ cnt[x]=1; f[x][1][1]=w[x]-w1[x],f[x][1][0]=w[x];f[x][0][0]=0; for(int p=head[x];p;p=ed[p].next){ int j=ed[p].to; dfs(j); for(int i=cnt[x];i>=0;i--){ for(int k=0;k<=cnt[j];k++){ f[x][i+k][1]=min(f[x][i+k][1],min(f[j][k][1],f[j][k][0])+f[x][i][1]); f[x][i+k][0]=min(f[x][i+k][0],f[j][k][0]+f[x][i][0]); } } cnt[x]+=cnt[j]; } } int main(){ n=read();b=read(); w[1]=read();w1[1]=read(); int z; for(register int i=2;i<=n;i++){ w[i]=read();w1[i]=read();z=read(); add(z,i); } memset(f,0x3f,sizeof(f)); dfs(1); for(register int i=n;i>=0;i--){ if(f[1][i][1]<=b||f[1][i][0]<=b){ cout<<i; return 0; } } }
首先让我最懊悔的一点:
jj这个剪枝,因为用了除法,所以导致出现了/0的情况,而且还没什么大用(非常好剪枝,让我的大脑疯狂旋转)。
其次:
这是这个题对于我来说最重要的一点:
可以看出题解中的DP是从前往后推过去的,而我的是从后往前利用前面逆推的,这就导致我的cnt数组其实每次都会多一些,而且我是提前算好的,没有具体划分每个儿子,而题解中划分了。不要看不起这一小点,如果不这么改还真的会大T特T。
逆天
第二题说完了。
然后这最后一题吧,额,只能说是跟屎一样出的非常的好,非常的具有挑战性,非常的具有前瞻性啊,上来一看题解是0/1分数规划直接就给我整不会了。
这个得分率也是非常的人性化,最高分20分,纯拿单调队列一遍一遍扫的,也有的可以特判大样例骗到5分。全场只有4个20的和一个10的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)