20161101的考试】搜索,搜索+dp,ds水题

本文分享了三道竞赛编程题目解答过程,包括汉诺塔问题的暴力搜索策略、矩阵折叠求最大值的动态规划方法及子树修改查询问题的线段树优化方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看到T2就觉得去年考过……似乎去年的我也在磕T3【

啊…………今天啊,解锁了新成就【考到一半蓝屏】

……T3在对拍,然后T1RE了,正在这个时候,老师打开了教师机,GG


……还好T3存了档……期望100,得分100


T1:

题面:汉诺塔升级了:现在我们有n个圆盘和n个柱子, 每个圆盘大小都不一样,大的圆盘不能放在小的圆盘上面, n个柱子从左到右排成一排。 每次你可以将一个柱子上的最上面的圆盘移动到右边或者左边的柱子上(如果移动之后是合法的话)。 现在告诉你初始时的状态, 你希望用最少的步数将第i小的盘子移动到第i个柱子上, 问最小步数。 n<=7

思路:暴搜啊不怂,从目标状态bfs得到所有状态的答案,然后直接输出答案即可(毕竟是多组询问2333)……注意一下模数和数组大小【才不说自己数组开小了wa了一年x

代码:

#include<bits/stdc++.h>
#define MAXM 10000000
#define INF 0x3f3f3f3f
using namespace std;	int T,N;

int cf8[10];

int f[10][MAXM];

int pos[10];

inline int hash(int b){
	int rtn=0;
	for(register int i=1;i<=b;++i)
		rtn+=cf8[i-1]*pos[i];
	return rtn;
}

inline void inv(int b,int x){
	for(register int i=1;i<=b;++i){
		pos[i]=x%8;
		x/=8;
	}
}

int a[10][10],top[10];

queue<int> q;
void bfs(int x){
	for(register int i=1;i<=x;++i)	pos[i]=i;
	int now=hash(x),aim;
	q.push(now);
	f[x][now]=0;
	
	while(!q.empty()){
		now=q.front();
		q.pop();
		inv(x,now);
		
		memset(top,0,sizeof top);
		for(register int i=x;i;--i)	a[pos[i]][++top[pos[i]]]=i;
		
		if(top[1] && (a[1][top[1]] < a[2][top[2]] || (!top[2]))){
			aim = now + cf8[a[1][top[1]]-1];
			if(f[x][aim] == -1)
				f[x][aim] = f[x][now]+1 , q.push(aim);	
		}
		if(top[x] && (a[x-1][top[x-1]] > a[x][top[x]] || (!top[x-1]))){
			aim = now - cf8[a[x][top[x]]-1];
			if(f[x][aim] == -1)
				f[x][aim] = f[x][now]+1 , q.push(aim);
		}
		
		for(register int i=2;i<x;++i){
			if(!top[i])	continue;
			int k=a[i][top[i]];
			if(a[i-1][top[i-1]] > k || !top[i-1]){
				aim = now - cf8[k-1];
				if(f[x][aim] == -1)
					f[x][aim] = f[x][now]+1 , q.push(aim);
			}
			if(a[i+1][top[i+1]] > k || !top[i+1]){
				aim = now + cf8[k-1];
				if(f[x][aim] == -1)
					f[x][aim] = f[x][now]+1 , q.push(aim);
			}
		}
	}
}

inline void init(){
	memset(f,-1,sizeof f);
	cf8[0]=1;
	for(register int i=1;i<8;++i)	cf8[i]=cf8[i-1]*8;
	f[1][1]=0;
	for(register int i=2;i<=7;++i)
		bfs(i);
}

int rd[10];
int rr[10];
inline int get_hash(int x){
	for(register int i=1;i<=x;++i)
		scanf("%d",rd+i),pos[i]=rd[i];
	sort(rd+1,rd+x+1);
	for(register int i=1;i<=x;++i)
		a[i][1] = lower_bound(rd+1,rd+x+1,pos[i]) - rd;
	for(register int i=1;i<=x;++i)	pos[a[i][1]]=i;
	return f[x][hash(x)];
}

int main(){
	freopen("huakai.in","r",stdin);
	freopen("huakai.out","w",stdout);

	init();
	
	scanf("%d",&T);
	while(T--){
		scanf("%d",&N);
		printf("%d\n",get_hash(N));
	}
	
	return 0;
} 
T2:

题面:现在有一个被1 × 1的小格子分割的矩形纸片(n*m,n<=20,m<=500),每个小格子内包含一个整数。现在你可以进行一系列的折叠,每次折叠的折痕必须是分割两行或者两列小格子的分割线。在折叠完之后,所有重叠的小格子被看作一个单独的格子,并且这个格子的价值为重叠的小格子的价值和。你想要知道,在所有可能得到的新格子中,格子价值的最大值为多少。
思路:n这么小直接枚举,2^20,再在m上dp,考虑只有奇偶性不同的可以转移,完了

代码:

#include<bits/stdc++.h>
#define MAXN 23
#define MAXM 505
using namespace std;	int n,m;

int a[MAXN][MAXM];

int sum[MAXM];

int ans=-1000000000;

int f[MAXM];
int mx[2];
void dfs(int now){
	mx[0]=mx[1]=-1000000000;
	for(register int i=1;i<=m;++i)	sum[i]+=a[now][i];
	for(register int i=1;i<=m;++i){
		f[i]=sum[i];
		f[i] += max(0, mx[(i&1)^1]);
		ans = max(ans,f[i]);
		mx[i&1] = max(mx[i&1],f[i]);
	}
	for(register int i=now+1;i<=n;++++i)	dfs(i);
	for(register int i=1;i<=m;++i)	sum[i]-=a[now][i];
}

int main(){
	freopen("taritari.in","r",stdin);
	freopen("taritari.out","w",stdout);

	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;++i)
		for(register int j=1;j<=m;++j)
			scanf("%d",&a[i][j]);

	for(register int i=1;i<=n;++i)	dfs(i);

	printf("%d",ans);
	return 0;
}

T3:

题面:因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为0。当城市x加派了k名士兵时。城市x所有子城市需要被加派k + 1名士兵。 这些子城市的所有子城市需要被加派k + 2名士兵。以此类推。当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可能询问以城市 i 为根的子树中的所有城市共被加派了多少士兵。你现在是国王的军事大臣,你能回答出国王的每个询问么?

思路:显然是个子树修改子树查询的裸题,然而发现似乎不太好维护,yy一下发现贡献就是 k*size 加上深度之差,直接线段树维护两个值就好了

代码:

#include<bits/stdc++.h>
#define MAXN 50005
using namespace std;	int n,p;
struct t1{
	int to,nxt;
}edge[MAXN<<1];	int cnt_edge=0;
int fst[MAXN];
inline void addedge(int x,int y){
	edge[++cnt_edge].to=y;
	edge[cnt_edge].nxt=fst[x];
	fst[x]=cnt_edge;
	
	edge[++cnt_edge].to=x;
	edge[cnt_edge].nxt=fst[y];
	fst[y]=cnt_edge;
}

int fth[MAXN],dpt[MAXN],siz[MAXN];
int dfn[MAXN],idf[MAXN],cnt_dfs;
void dfs(int now){
	siz[now]=1;
	dfn[now]=++cnt_dfs;
	idf[cnt_dfs]=now;
	for(register int tmp=fst[now];tmp;tmp=edge[tmp].nxt){
		int aim=edge[tmp].to;
		if(aim==fth[now])	continue;
		fth[aim]=now;
		dpt[aim]=dpt[now]+1;
		dfs(aim);
		siz[now]+=siz[aim];
	}
}
//===========================================================
long long tag_v[MAXN<<2],tag_d[MAXN<<2],tag_cnt[MAXN<<2];
long long sum_dpt[MAXN<<2],sum_siz[MAXN<<2],dt[MAXN<<2];

void build(int now,int l,int r){
	if(l==r){
		sum_dpt[now]=dpt[idf[l]];
		sum_siz[now]=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(now<<1,l,mid);
	build(now<<1|1,mid+1,r);
	sum_dpt[now]=sum_dpt[now<<1]+sum_dpt[now<<1|1];
	sum_siz[now]=r-l+1;
}

inline void pushdown(int now){
	dt[now<<1] += 
		sum_siz[now<<1] * tag_v[now] 
		+ sum_dpt[now<<1] * tag_cnt[now] 
		- sum_siz[now<<1]*tag_d[now];
	dt[now<<1|1] += 
		sum_siz[now<<1|1] * tag_v[now] 
		+ sum_dpt[now<<1|1] * tag_cnt[now] 
		- sum_siz[now<<1|1]*tag_d[now];
	tag_v[now<<1] += tag_v[now];
	tag_v[now<<1|1] += tag_v[now];
	tag_d[now<<1] += tag_d[now];
	tag_d[now<<1|1] += tag_d[now];
	
	tag_cnt[now<<1] += tag_cnt[now];
	tag_cnt[now<<1|1] += tag_cnt[now];
	tag_v[now]=tag_d[now]=tag_cnt[now]=0;
}

void modify(int now,int l,int r,int L,int R,int v,int d){
	if(tag_d[now])	pushdown(now);
	if(L<=l&&r<=R){
		dt[now] += sum_siz[now]*v + sum_dpt[now] - sum_siz[now]*d;
		tag_v[now] = v,tag_d[now] = d, tag_cnt[now]=1;
		return ;
	}
	int mid=(l+r)>>1;
	if(L<=mid)	modify(now<<1,l,mid,L,R,v,d);
	if(mid<R)	modify(now<<1|1,mid+1,r,L,R,v,d);
	dt[now]=dt[now<<1]+dt[now<<1|1];
}

long long inqry(int now,int l,int r,int L,int R){
	if(L<=l&&r<=R)
		return dt[now];
	int mid=(l+r)>>1;
	if(tag_d[now])
		pushdown(now);
	long long rtn=0;
	if(L<=mid)	rtn=inqry(now<<1,l,mid,L,R);
	if(mid<R)	rtn+=inqry(now<<1|1,mid+1,r,L,R);
	return rtn;
}

//===========================================================
int read_x,read_y,read_k;
char opt[10];
int main(){
	freopen("truetears.in","r",stdin);
	freopen("truetears.out","w",stdout);
	
	scanf("%d%d",&n,&p);
	for(register int i=2;i<=n;++i){
		scanf("%d",&read_x);
		addedge(i,read_x);
	}
	
	dpt[1]=1;
	dfs(1);
	
//	for(int i=1;i<=n;++i)	printf("%d\ndpt = %d   siz = %d\n",i,dpt[i],siz[i]);	
	
	build(1,1,n);
	
	while(p--){
		scanf("%s",opt);
		if(opt[0]=='Q'){
			scanf("%d",&read_x);
			printf("%lld\n",inqry(1,1,n,dfn[read_x],dfn[read_x]+siz[read_x]-1));
		}
		else{
			scanf("%d%d",&read_x,&read_y);
			modify(1,1,n,dfn[read_x],dfn[read_x]+siz[read_x]-1,read_y,dpt[read_x]);
		}
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值