2019-10-24CSP-S模拟

先来一波反思

T1 没有注意到早餐肠和晚餐肠随机选择其中分别绝对值最小的,导致以为每次从所有的肠里选绝对值最小的,于是开心的码了一波代码,光荣gg。

总结 以后做题一定要好好看题目细节描述,千万不要想当然就写代码,不然会死很惨

T2 当时想到了负负得正,根据符号来判断在哪里加括号的,可惜DP太菜并没有清楚地找到转移方程看了题解才恍然大悟,我果然是太菜了

总结 对于美妙毒瘤的DP一定要多做题,最好在草稿纸上多画几个例子分析分析,状态转移方程最好把概念理清楚再写,不然会把自己搞死

T3 首先暴露了自己对于STL库中各种自带数据结构的使用极其不熟练这个缺点,线段树我也是菜的一* 以后一定要多写写(数据结构不好真的很吃亏啊啊啊啊,毕竟中国是数据结构强国)。当时读题其实读的很清楚,但是写着写着心态莫名崩塌导致细节写晕,送的分都要不起。。心态问题还是要多锻炼。

总结 再一次强调草稿纸的重要性,多画画图举例子不就很清楚了吗QAQ,虽然我当时一开始用例子确实理解清楚题意了,但是做着做着就把题忘了可能这就是zz吧,要多读几次题,搞清细节,不能慌,仔细分析题意,把该拿的分拿到,保证不出现zz过失。

以上就是此次考试的反思,希望以后的反思能越来越简洁,不要再犯这么多zz错误了,调整好状态,我可以!!!

T1

提交地址
题目

思路

Tom每天会随机吃一顿饭,可能是早饭,也可能是晚饭。如果是吃早饭,Tom会吃掉编号绝对值最小的早餐肠,反之吃掉编号绝对值最小的晚餐肠。 这句话仿佛在暗示我们早餐肠和晚餐肠要分开标号
如下图:如果Tom今天想吃早饭了吃了个②,那这棵树就凉了。在这里插入图片描述
于是我们得到了一个思路:
正数和负数编号各自组成了一个联通块,并且-b和+a必须相邻(吃绝对值最小or最大)
做法:我们找到一条边,使这条边一端是以-b为根的子树,一条边是以+a为根的子树,然后dfs向下按照dfs序标号即可
上代码

#include <bits/stdc++.h>
using namespace std;
const int N=(int)1e5+500;
int t,n,a,b,num1,num2;
int siz[N],id[N];
int fa[N];
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')	f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
int fir[N],nxt[N<<1],tot,to[N<<1];
void add(int x,int y){
	nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;
}
void gsize(int x,int f){
	siz[x]=1,fa[x]=f;
	for(int i=fir[x];i;i=nxt[i]){
		if(to[i]==f)	continue;
		gsize(to[i],x);siz[x]+=siz[to[i]];
	}
}
void color(int x,int fa,int *val,int del){
	for(int i=fir[x];i;i=nxt[i]){
		if(to[i]!=fa){
			color(to[i],x,val,del);
		}
	}
	(*val)+=del;//指针的妙用:方便直接修改值,传递地址 
	id[x]=(*val);
}
int main(){
		n=read();num1=read(),num2=read();
		for(int i=1;i<n;i++){
			a=read(),b=read();
			add(a,b);add(b,a);
		}
		gsize(1,0);
		bool ok=0;
		for(int i=1;i<=n;i++){
			if(siz[i]==num1){
				int x=0;
				color(i,fa[i],&x,1);//正着向下染一棵a树
				x=0;
				color(fa[i],i,&x,-1);//反着向上染一棵b树 
				ok=1;
			}
			if(siz[i]==num2){
				int x=0;
				color(i,fa[i],&x,-1);//此处用了指针,方便修改值 
				x=0;
				color(fa[i],i,&x,1);
				ok=1;
			}
		}
		if(!ok)	return puts("-1"),0;
		for(int i=1;i<=n;i++){
			printf("%d ",id[i]);
		}
		printf("\n");
	return 0;
}

注意
在这里插入图片描述
可能根节点在哪个子树中(或是哪个子树的根),而它的子树大小不是num1或者num2,故处理完子树大小开始染色时要对num1和num2都进行处理,而不是只处理一个就完了。
ps:因为我们每次染色都使用指针重新赋值,不用担心最后值出现问题。

T2

提交地址
在这里插入图片描述
有一条浅显的性质:负负得正
于是得到本题思路:考虑在每一个负号前是添加括号更有还是不添加负号更优,得到dp方程
设seq表示当前序列的数, f i , j f_{i,j} fi,j表示,当前到了序列的第 i i i位,还有 j j j个左括号没有被匹配。
因为在 + + +前补括号相当于没补,所以我们只探讨在负号前补括号的情况,由负负得正考虑符号反向,使加的数最大即可最优
注意:处理时记得 s e q [ i ] seq[i] seq[i]是一个带符号的数
如果当前数 > 0 >0 >0(符号为正)
f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 0 ] + s e q [ i ] , f [ i − 1 ] [ 1 ] + s e q [ i ] , f [ i − 1 ] [ 2 ] + s e q [ i ] ) ; f[i][0]=max(f[i-1][0]+seq[i],f[i-1][1]+seq[i],f[i-1][2]+seq[i]); f[i][0]=max(f[i1][0]+seq[i],f[i1][1]+seq[i],f[i1][2]+seq[i]);
f [ i ] [ 1 ] = m a x ( f [ i − 1 ] [ 1 ] − s e q [ i ] , f [ i − 1 ] [ 2 ] − s e q [ i ] ) ; f[i][1]=max(f[i-1][1]-seq[i],f[i-1][2]-seq[i]); f[i][1]=max(f[i1][1]seq[i],f[i1][2]seq[i]);
f [ i ] [ 2 ] = f [ i − 1 ] [ 2 ] + s e q [ i ] ; f[i][2]=f[i-1][2]+seq[i]; f[i][2]=f[i1][2]+seq[i];
如果当前数 < 0 <0 <0(符号为负)
f [ i ] [ 0 ] = − I N F ; / / 这 种 情 况 不 可 能 存 在 , 故 转 移 决 策 时 不 可 能 取 到 f[i][0]=-INF;//这种情况不可能存在,故转移决策时不可能取到 f[i][0]=INF;//
f [ i ] [ 1 ] = m a x ( f [ i − 1 ] [ 0 ] + s e q [ i ] , f [ i − 1 ] [ 1 ] + s e q [ i ] , f [ i − 1 ] [ 2 ] + s e q [ i ] ) ; / / 不 管 怎 么 样 那 个 数 该 减 还 是 要 被 减 f[i][1]=max(f[i-1][0]+seq[i],f[i-1][1]+seq[i],f[i-1][2]+seq[i]);//不管怎么样那个数该减还是要被减 f[i][1]=max(f[i1][0]+seq[i],f[i1][1]+seq[i],f[i1][2]+seq[i]);//
f [ i ] [ 2 ] = m a x ( f [ i − 1 ] [ 1 ] − s e q [ i ] , f [ i − 1 ] [ 2 ] − s e q [ i ] ) ; / / 负 负 得 正 f[i][2]=max(f[i-1][1]-seq[i],f[i-1][2]-seq[i]);//负负得正 f[i][2]=max(f[i1][1]seq[i],f[i1][2]seq[i]);//

代码
#include <bits/stdc++.h>
#define int long long
#define maxn(x,y,z) max(max(x,y),z)
using namespace std;
const int N=500050;
int n,seq[N],f[N][4],t;
int INF=(int)-1e18;
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')	f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
signed main(){
	t=read();
	while(t--){
		n=read();
		for(int i=1;i<=n;i++){
			seq[i]=read();
		}
		f[0][0]=0,f[0][1]=f[0][2]=INF;
		for(int i=1;i<=n;i++){
			if(seq[i]>0){
				f[i][0]=maxn(f[i-1][0]+seq[i],f[i-1][1]+seq[i],f[i-1][2]+seq[i]);
				f[i][1]=max(f[i-1][1]-seq[i],f[i-1][2]-seq[i]);
				f[i][2]=f[i-1][2]+seq[i];
			}
			else{
				f[i][0]=INF;
				f[i][1]=maxn(f[i-1][0]+seq[i],f[i-1][1]+seq[i],f[i-1][2]+seq[i]);
				f[i][2]=max(f[i-1][1]-seq[i],f[i-1][2]-seq[i]);
			}
		}
		printf("%lld\n",maxn(f[n][0],f[n][1],f[n][2]));
	}
	return 0;	
}

T3

提交地址
在这里插入图片描述
手玩几组样例,发现 x x x坐标是单调不减的,且我们只用维护矩形的左边界(因为 x x x不走回头路,路径直接加上 X t X_t Xt即可),路径要拐弯只可能在矩形边界处拐弯(达到最优),所以我们考虑维护当前所处位置 X i , [ l , r ] X_i,[l,r] Xi,[l,r] y y y坐标上的前驱(最小的最大)和后继(最大的最小),中间因为反正也不考虑走进矩形,直接不管。

思路:考虑维护每个 X i X_i Xi区间的 l , r l,r l,r,放进一个 s e t set set里, ( l , r ) (l,r) (l,r)的值都标为 I N F INF INF,只维护区间端点就可以了。
代码(抄的,我咕了我过几天再自己写代码)

#include <bits/stdc++.h>

using namespace std;

const int MX = 500005;
const int oo = 1000000000;

template <typename T> void read(T &x)
{
	x = 0; char c = getchar(); bool f = 0;
	while(!isdigit(c) && c!='-') c = getchar();
	if(c == '-') f = 1, c = getchar();
	while(isdigit(c)) x = x*10+c-'0', c = getchar();
	if(f) x = -x;
}

struct NODE
{
	int x, y, d;
	
	NODE (const int &x0 = 0, const int &y0 = 0, const int &d0 = 0) : x(x0), y(y0), d(d0) {}
	
	bool operator < (const NODE &t) const {return y < t.y;}
};

struct REC
{
	int l, r, d, u;
	
	REC (const int &l0 = 0, const int &r0 = 0, const int &d0 = 0, const int &u0 = 0) : l(l0), r(r0), d(d0), u(u0) {}
	
	void adjust()
	{
		if(l > r) swap(l, r);
		if(d > u) swap(d, u);
	}

	bool operator < (const REC &t) {return l < t.l;}
};

int n, tx, ty;
REC obs[MX];

void input()
{
	read(n);
	read(tx), ty = 0;
	for(int i=1; i<=n; i++)
	{
		read(obs[i].l), read(obs[i].d);
		read(obs[i].r), read(obs[i].u);
		obs[i].adjust();
	}
}

int dis(int x1, int y1, int x2, int y2)
{
	return abs(x1-x2) + abs(y1-y2);
}

int calc()
{
	set<NODE> st;
	sort(obs+1, obs+n+1);
	st.insert(NODE(0, 0, 0));
	for(int i=1; i<=n; i++)
	{
		set<NODE>::iterator itl, itr, del;
		int mnd = +oo, mnu = +oo;
		itl = st.lower_bound(NODE(obs[i].l, obs[i].d, 0));
		itr = st.upper_bound(NODE(obs[i].l, obs[i].u, 0));
		while(itl != itr)
		{
			del = itl;
			mnd = min(mnd, dis(del->x, del->y, obs[i].l, obs[i].d) + del->d);
			mnu = min(mnu, dis(del->x, del->y, obs[i].l, obs[i].u) + del->d);
			itl++;
			st.erase(del);
		}
		st.insert(NODE(obs[i].l, obs[i].d, mnd));
		st.insert(NODE(obs[i].l, obs[i].u, mnu));
	}
	int ret = +oo;
	for(set<NODE>::iterator it = st.begin(); it!=st.end(); it++)
		ret = min(ret, dis(it->x, it->y, tx, ty) + it->d);
	cerr<<ret<<endl;
	return ret;
}

void work()
{
	printf("%d\n", calc());
}

int main()
{
	freopen("speike.in", "r", stdin);
	freopen("speike.out", "w", stdout);
	input();
	work();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值