8.27考试整理

木工(dp)

作为一代神犇 WXR,准备在暑假学习 LRJ,通过卖家具来发家致富。但是他
手艺不精,最后还是只能为别人造栅栏。有一天,他忽然想到,如果一块木板可
做出很多种栅栏的话,他也许就可以发家致富了。于是,他在想,如果他有一块
n 的木板,有多少种方法截成 4 段长为整数的段,它们可以围成一个四边形呢
注意:如果两种方式中,有一点的切割位置不同,则算作两种不同的方案。
要担心排除对称方案,或其他像这样复杂的问题。
【输入】
一个整数 n
【输出】
方案数
【样例数据】
wood.in
6
wood.out
6
【数据范围】
对于 30%的数据,n 不大于 100

对于 100%的数据,n 不大于 10000



本题DP,由此发现我DP功底不强。(本题要复习思路)

f[i][j]表示截断到第i段(i从0起步),总长度为j的方案数。

还有注意的一点是:四边形的任意一条边的长度不能大于等于周长的一半


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

#define LL long long
#define MAXN (10000+5)

LL f[4][MAXN];

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

	int n;
	scanf("%d", &n);
	
	for(int i = 1; i <= (n+1)/2-1; i++) f[0][i] = 1;
	for(int i = 1; i <= 3; i++)
	  for(int j = i+1; j <= n; j++){
	      int r = min(j-1, (n+1)/2-1);
	  	  for(int k = 1; k <= r; k++) f[i][j] += f[i-1][j-k];
	  }
	  
	printf("%lld\n", f[3][n]);

	return 0;
}

2. 舞会
为了庆祝外星人鼠标上市,公司老总 WXR(就是上题中那个神犇)举办
了一场舞会,舞会的一项活动是用鼠标操控灯。LZ 表示一看操控的循序就可以
知道最后灯的亮灭,你为了不被鄙视,以及不被鄙视,必须在 LZ 之前算出灯的
亮灭。这样你就可以鄙视他了。
灯是由高科技——外星人鼠标操控的。你只要左击两个灯所连的鼠标,
这两个灯,以及之间的灯都会由暗变亮,或由亮变暗。右击两个灯所连的鼠
标,你就可以知道这两个灯,以及之间的灯有多少灯是亮的。你的任务是在 LZ
之前算出灯的亮灭。
【输入】
1 : 用空格隔开的两个整数 N Mn 是灯数
2..M+1 : 每行表示一个操作, 有三个用空格分开的整数: 指令号, S_i E_i
1 种指令( 0 表示)包含两个数字 S_i E_i (1 <= S_i <= E_i <= N), 它们表示起
始开关和终止开关. 表示左击
2 种指令( 1 表示)同样包含两个数字 S_i E_i (1 <= S_i <= E_i <= N), 不过这
种指令是询问从 S_i E_i 之间的灯有多少是亮着的.
【样例数据】
party.in
4 5
0 1 2
0 2 4
1 2 3
party.out
1 2
(1<=n<= 100000

(1 <= m <=1000000)


线段树,不过汲取经验:rev 改变时只能用^, 不能直接赋值,因为它可能以前被反转过。

<span style="color:#000000;">#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

#define MAXN (100000+5)

int ql, qr;

struct Seg_tree{
	int sum[MAXN*4];
	bool rev[MAXN*4];
	
	void push_up(int o){
		int lc = o<<1, rc = o<<1|1;
		sum[o] = sum[lc]+sum[rc];
	}
	
	void push_down(int o, int L, int R){
		int lc = o<<1, rc = o<<1|1;
		int mid = (L+R)>>1;
		int ll = L, lr = mid, rl = mid+1, rr = R;
		
		if(rev[o]){
			sum[lc] = (lr-ll+1) - sum[lc];
			sum[rc] = (rr-rl+1) - sum[rc];
			rev[lc] ^= 1;
			rev[rc] ^= 1;
			rev[o] = false;
		}
	}
	
	void update(int o, int L, int R){
		if(ql <= L && qr >= R){
			sum[o] = (R-L+1) - sum[o]; rev[o] ^= 1;
			return;
		}
		
		push_down(o, L, R);
		int lc = o<<1, rc = o<<1|1;
		int mid = (L+R)>>1;
		
		if(ql <= mid) update(lc, L, mid);
		if(qr > mid) update(rc, mid+1, R);
		
		push_up(o);
	}
	
	int query(int o, int L, int R){
		if(ql <= L && qr >= R) return sum[o];

		push_down(o, L, R);
		int ret = 0;
		int lc = o<<1, rc = o<<1|1;
		int mid = (L+R)>>1;
			
		if(ql <= mid) ret += query(lc, L, mid);
		if(qr > mid) ret += query(rc, mid+1, R);
		
		return ret;
	}
};

Seg_tree ST;

int main(){
	freopen("party.in", "r", stdin);
	freopen("party.out", "w", stdout);
	
	int n, m;
	scanf("%d%d", &n, &m);
	
	for(int i = 1; i <= m; i++){
		int op;
		scanf("%d%d%d", &op, &ql, &qr);
//		printf("op = %d\n", op);
		
		if(!op) ST.update(1, 1, n);
		else printf("%d\n", ST.query(1, 1, n));
//		printf("____________________\n");
	}
	
	return 0;
}</span>


3. 几何
WXR 是个爱 NP 问题的孩子,有一天他发现了一个问题:已知一个平面
n+1 个点,有 n 个点在 x 轴上,求以点 k 开始的最短哈密顿路。WXR 思考了
很久,依旧没有想出来。于是他简化了这道题,改为最短的,以 k 开始的,遍
历所有点的路的长度。不过,WXR 在做 NP 问题时已经用尽了精力,于是他把
问题交给了他的机智的下属,你。
【输入】
第一行有两个数 nk
第二行有 n 个数,表示在 x 轴上的点的横坐标
第三行有两个数,表示不在 x 轴上的点的坐标
【输出】
只有一行,为最短的路长度
【样例数据】
picture.in
3 1
0 1 2
1 1
picture.out
3.4142135624
【数据范围】
对于 100%的数据,1n100000
【提示】
k=n+1 表示起始点是那个不在 x 轴上的点
比较方式是 lemon 的实数比较方式,实数精度为 7,标准数据保留 10 位小
数。

思路是枚举走上去的点,然后从k先走到那个点,再走下来,细节有时间再补充。(本题要复习思路)


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

#define MAXN (100000+5)

double a[MAXN];
double mx, x, y;

double dis(int i){
	return sqrt((x-a[i])*(x-a[i]) + (y*y));
}

double cas1(int L, int R){
	return a[R] - a[L] + min(dis(L), dis(R));
}

double cas2(int L, int R){
	return a[R] - a[L] + min(dis(L)+abs(mx-a[R]), dis(R)+abs(mx-a[L]));
}

int main(){
	freopen("picture.in", "r", stdin);
	freopen("picture.out", "w", stdout);
	
	int n, k;
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i++) scanf("%lf", &a[i]);
	scanf("%lf%lf", &x, &y);
	
	mx = a[k];
	sort(a+1, a+n+1);
	
	double ans;
	if(k == n+1) ans = cas1(1, n);
	else{
		ans = cas2(1, n);
		for(int i = 1; i < n; i++)
		  ans = min(ans, min(cas1(1, i)+cas2(i+1, n), cas2(1, i)+cas1(i+1, n)));
	}
	
	printf("%.10lf\n", ans);
	
	return 0;
}

微笑 逆水行舟,不进则退

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值