木工(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 和 M,n 是灯数
第 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 问题时已经用尽了精力,于是他把
问题交给了他的机智的下属,你。
【输入】
第一行有两个数 n、k。
第二行有 n 个数,表示在 x 轴上的点的横坐标
第三行有两个数,表示不在 x 轴上的点的坐标
【输出】
只有一行,为最短的路长度
【样例数据】
picture.in
3 1
0 1 2
1 1
picture.out
3.4142135624
【数据范围】
对于 100%的数据,1≤n≤100000
【提示】
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;
}
逆水行舟,不进则退