考试后总结

今天oi又考试了。
记得考试前huge说:”明天咱们出点简单的题啊,起码 二百大几 保底啊,努力努力AK了也有不是没可能啊......“
那我是直接就被2、4题按在地上摩擦了。
先看第2题:
image

本来看出来了是个树形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的。

posted @ 2024-02-23 19:43  lzrG23  阅读(20)  评论(0编辑  收藏  举报