NOIP大纲整理:(十六)反转问题与弹性碰撞
一、反转问题
算法概览:给定一个01串,现有翻转规则:翻转某一个位置时其后面2个位置也会跟着翻转,也就是每次翻转都会翻转3个连续的位置。要将01串全部翻转为0,求最小的翻转次数。形似这类题的问题叫做反转问题,也可以叫开关问题,对于这类题通常有以下的特点,思考一下就可以想到。
1、若某一个位置被翻转了n次,则其实际上被翻转了n%2次,因为翻转2k次相当与没翻转,翻转2k+1次相当于翻转了1次,因为要求最小翻转次数,所以对于某一个位置要么只(主动)翻转一次,要么不(主动)翻转。
2、分析易知翻转的顺序并不影响最终结果。(理解不了可自己举个例子在纸上模拟下)
3、由此,反转问题(也叫开关问题)就转化成求反转区间的集合,常常和枚举一起使用。
POJ 3276
N头牛排列成了一列,每头牛或者向前或者向后站,为了让所有的牛都面向前方,农夫约翰买了一台自动转向的机器,这个机器在购买时就必须设定一个数值K,机器每操作一次恰好使K头连续的牛转向(K头牛分别为当前的牛及其之后的牛,不影响位于它之前的牛,并且每次反转必须是K头牛,不可以少于K头)。请求出为了让所有的牛都能面向前方需要的最少的操作次数M和对应的最小的K。
已知:
1≤N≤5000
sampleinput
N= 7
BBFBFBB(F:面向前方,B:面向后方)
sampleoutput
K= 3
M= 3
(先反转1~3号的三头牛,然后再反转3~5号,最后反转5~7号)
思路:
定义 f[i]:区间[i,i+k-1]进行反转的话就为1,否则为0
区间反转部分很好优化:
在考虑第i头牛时候,如果∑i−1j=(i−K+1)f[j]和为奇数,就说明此时这个牛方向与最初相反。
由于 ∑ij=(i+1)−K+1f[j]=∑i−1j=(i−K+1)f[j]+f[i]-f[i-K+1]
所以这个每一次都可以用常数算出来,时间复杂度O(n^2)
#include<cstdio>
#include<cstring>
#include<iostream>
using name space std;
const int N=5000+10;
int f[N],dir[N],n;
int solve(int k){
int cnt=0,sum=0;//sum为f的和
memset(f,0,sizeof(f));
for(int i=1;i<=n-k+1;i++){
if((dir[i]+sum)%2){
cnt++;
f[i]=1;
}
sum+=f[i];
if(i-k+1>=1) sum-=f[i-k+1];
}
for(int i=n-k+2;i<=n;i++){//检查剩下的牛有没有朝后面的情况
if((dir[i]+sum)%2) return n+1;
if(i-k+1>=1) sum-=f[i-k+1];
}
return cnt;
}
int main(){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++){
char c;scanf(" %c",&c);
if(c=='B') dir[i]=1;
}
int ansk,ansm=n,t;
for(int i=1;i<=n;i++){
t=solve(i);
if(t<ansm){
ansm=t;ansk=i;
}
}
printf("%d %d\n",ansk,ansm);
}
}
POJ 3279
农夫约翰知道聪明的牛产奶多。于是为了提高牛的智商他准备了如下游戏。有一个M×N 的格子,每个格子可以翻转正反面,它们一面是黑色,另一面是白色。黑色的格子翻转后就是白色,白色的格子翻转过来则是黑色。游戏要做的就是把所有的格子都翻转成白色。不过因为牛蹄很大,所以每次翻转一个格子时,与它上下左右相邻接的格子也会被翻转。因为翻格子太麻烦了,所以牛都想通过尽可能少的次数把所有格子都翻成白色。现在给定了每个格子的颜色,请求出用最小步数完成时每个格子翻转的次数。最小步数的解有多个时,输出字典序最小的一组。解不存在的话,则输出IMPOSSIBLE。
已知:
1≤M,N≤15
sample input
M= 4
N= 4 每个格子的颜色如下:(0表示白色,1表示黑色)
10 0 1
01 1 0
01 1 0
10 0 1
sample output
00 0 0
10 0 1
10 0 1
00 0 0
思路:在上面的那道题,让最左端的奶牛反转的情况只有一种,于是直接判断的方法就可以确定,但是这里不一样,比如,看最左上面的角,除了反转(1,1),(1,2),(2,1)都可以导致他翻装。
于是不妨我们先确定最上面一行的反装方式,此时能反转(1,1)只有(2,1),
所以如果已知第一行就可以知道第二行那些点需要反转。这样反复下去,只要最后一行全部为白,就说明可行。
那么这个算法时间复杂度是(N*M*2^N).
#include<cstdio>
#include<cstring>
using name space std;
const int dx[5]={-1,0,0,0,1};
const int dy[5]={0,-1,0,1,0};
int m,n,M[20][20],tmp[20][20],ans[20][20],cnt;
int get(int x,int y){
int t=M[x][y];
for(int i=0;i<5;i++){
int tx=dx[i]+x,ty=dy[i]+y;
if(tx>=0&&tx<m&&ty>=0&&ty<n)t+=tmp[tx][ty];
}
return t%2;
}
int cal(){
for(int i=1;i<m;i++){
for(int j=0;j<n;j++){
if(get(i-1,j)) tmp[i][j]=1;
}
}
for(int j=0;j<n;j++){
if(get(m-1,j)!=0) return n*m+1;
}
int res=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
res+=tmp[i][j];
}
}
return res;
}
int main(){
while(~scanf("%d%d",&m,&n)){
cnt=n*m+1;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
scanf("%d",&M[i][j]);
}
}
for(int i=0;i<(1<<n);i++){
memset(tmp,0,sizeof(tmp));
for(int j=0;j<n;j++){
tmp[0][j]=i>>j&1;
}
int t=cal();
if(t<cnt){
cnt=t;
memcpy(ans,tmp,sizeof(tmp));
}
}
if(cnt==n*m+1){
printf("IMPOSSIBLE\n");
}
else{
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
printf("%d%c",ans[i][j],j+1==n ? '\n':' ');
}
}
}
}
}
POJ 3185
翻盖有奖:将一列碗翻成口朝上,一把下去可能同时反转3个或2个(首尾),求最小翻转次数。
思路:这题分析一下,可以知道选择从第一个开始翻,还是聪第二个开始翻,会导致两种不同的状态和结果,但都是唯一的。所以就枚举第一个还是第二个开始翻,然后从左往右依次判断,接下来每个点需不需要翻转取决它左边是不是朝下。
#include<cstdio>
#include<cstring>
#include<algorithm>
using name space std;
int dir[25],f[25];
int main(){
while(~scanf("%d",&dir[0])){
int tmp=0,ans=20;
memset(f,0,sizeof(f));
for(int i=1;i<20;i++)scanf("%d",&dir[i]);
f[0]=1;tmp++;
for(int i=1;i<20;i++){
if(f[i]=(f[i-2]^f[i-1]^dir[i-1]))tmp++;
}
if((f[18]^f[19]^dir[19])==0&&tmp<ans) ans=tmp;
tmp=0;f[0]=0;
for(int i=1;i<20;i++){
if(f[i]=(f[i-2]^f[i-1]^dir[i-1]))tmp++;
}
if(f[18]^f[19]^dir[19]==0&&tmp<ans) ans=tmp;
printf("%d\n",ans);
}
}
总结:往往反转问题(开关问题)可以转换成矩阵求解一组方程的解是否存在,用高斯消元求解,并且通过这些分析知道,当自由变员不超过N个时候,也可以用来求解最优解。
二、弹性碰撞
在理想情况下,物体碰撞后,形变能够恢复,不发热、发声,没有动能损失,这种碰撞称为弹性碰撞(elastic collision),又称完全弹性碰撞。生活中,硬质木球或钢球发生碰撞时,动能的损失很小,可以忽略不计,通常也可以将它们的碰撞看成弹性碰撞。
发生弹性碰撞后,两个物体在碰撞后相比碰撞前,相对速度大小相等方向相反;两个物体在发生弹性碰撞前后,动量的矢量和、能量的和都不变;如果两个物体质量相同,速度大小方向都互换,相当于未碰撞。
用N个半径为R厘米的球进行如下实验。
在H米高的位置设置一个圆筒,将求垂直放入(从下向上数第i个球的底端距离地面高度为H + 2R)。实验开始时最下面的球开始掉落,此后每一秒又有一个球开始掉落。不计空气阻力,并假设球与球或地面间的碰撞时弹性碰撞。
请求出实验开始后T秒时每个球底端的高度。假设重力加速度为g=10m/s2
已知:
1≤N≤100
1≤H≤10000
1≤R≤100
1≤T≤10000
sample input
N= 1
H= 10
R= 10
T= 100
sample output
4.95
从高位H的位置下落的话需要花费的时间:
因此,在时刻T时,令K 为满足kt≤T的最大整数,那么
当R = 0时,如果认为球是一样的,就可以忽视他们的碰撞,视为直接互相穿过继续运动。由于在有碰撞时球的顺序不会发生改变,所以忽略碰撞,将计算得到的坐标进行排序后,就能知道每个球的最终位置。
那么,R>0是要怎么样?这种情况下的处理方法基本相同,对于下方开始的第i个球,在按照R = 0计算的结果上加上2*R*i就可以了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#define sf scanf
#define pf printf
using name space std;
const int Maxn = 110;
double ans[Maxn];
int main()
{
int cas,N,H,R,T,g = 10;
sf("%d",&cas);
while(cas--)
{
sf("%d%d%d%d",&N,&H,&R,&T);
for(int i = 0;i < N;i ++)
{
double t = sqrt(2.0 * H / g);
int k = floor(T / t);
if(k < 0)
ans[i] = H;
else
{
if(k & 1)
ans[i] = H - 0.5 * g * (t -(T - k * t)) * (t - (T - k * t));
else
ans[i] = H - 0.5 * g * (T -k * t) * (T - k * t);
}
T --;
}
sort(ans,ans + N);
for(int i = 0;i < N;i ++)
pf("%.2lf%c",ans[i] + 2.0* R * i / 100,i + 1 == N ? '\n':' ');
}
return 0;
}
/*
2
110 10 100
210 10 100
*/
POJ 1852
题意:在一根长为L的水平木棍上有一群数量为n的蚂蚁,它们以每秒1cm/s的速度走到木棍一端就会掉下去。现在知道它们的起始位置是距离木根左端点的x处。但是不知道它们爬行的方向。在相向而行的两只蚂蚁相遇后,它们会掉头往反方向走。问所有蚂蚁都落下木棍的最快时间和最慢时间。
题解:一开始觉得可以暴搜,每只蚂蚁只有两种情况,不过掉头的事情感觉很复杂。时间复杂度为2的n次幂。肯定超时。 因为是同时出发的,相遇时的两只蚂蚁用的时间是相同的,我们可以无视蚂蚁的区别,当两只蚂蚁相遇时它们保持原样交错而行。这样每只蚂蚁都是独立运动的,那么只要找每只蚂蚁掉下去的时间就行了。
#include<cstdio>
#define maxn 1000100
int a[maxn],ansmin,ansmax,L,n;
int MIN(int a,int b)
{
return a<b?a:b;
}
intMAX(int a,int b)
{
return a>b?a:b;
}
void ansMIN()
{
int i,min;
ansmin=-1;
for(i=0;i<n;++i)
{
min=MIN(a[i],L-a[i]);
if(min>ansmin)
ansmin=min;
}
printf("%d ",ansmin);
}
void ansMAX()
{
int i,max;
ansmax=-1;
for(i=0;i<n;++i)
{
max=MAX(a[i],L-a[i]);
if(max>ansmax)
ansmax=max;
}
printf("%d\n",ansmax);
}
int main()
{
int i,t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&L,&n);
for(i=0;i<n;++i)
scanf("%d",&a[i]);
ansMIN();
//求所有蚂蚁掉下去的最短时间
ansMAX();
//求所有蚂蚁掉下去的最长时间
}
return 0;
}
NOIP大纲整理:(十六)反转问题与弹性碰撞相关推荐
- Python学习笔记整理(十六)类的设计
如何使用类来对有用的对象进行建模? 一.Python和OOP Python和OOP实现可以概括为三个概念. 继承 继承是基于Python中属性查找(在X.name表达式中) 多态 在X ...
- NOIP大纲整理:(零)历年2000-2016NOIP提高组题目分析
年份 题目名称 考查内容 难度 2000-2016年NOIP提高组复赛题目 2000-A 进制转换 初等代数,找规律 ★ 2000-B 乘积最大 资源分配DP ★★★ 2000-C 单词接龙 DF ...
- 整理第十六届全国大学智能车竞赛比赛数据
简 介: 整理提交了第十六届全国大学生智能车总决赛的数据. 关键词: 智能车竞赛,总决赛 #mermaid-svg-QA3wJFTY8KGVWeUO {font-family:"trebuc ...
- 从零开始自制实现WebServer(十六)---- 学习新工具CMake自动编写MakeFile 分门别类整理源文件心情愉悦
文章目录 全流程实现博客链接 前引 (十六)---- 学习新工具CMake自动编写MakeFile 小改小动项目接近尾声 1.学习新工具 cmake / shell脚本 需要耐心与时间 2.分门别类整 ...
- C++ Primer plus学习笔记-第十六章:string类和标准模板库
第十六章:string类和标准模板库 前言:这一章已经相当靠近全书的后面部分了:这一章我们会深入探讨一些技术上的细节,比如string的具体构造函数,比如适用于string类的几个函数,比如我们还会介 ...
- NeHe OpenGL教程 第二十六课:反射
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- 第七十六章 SQL命令 TOP
文章目录 第七十六章 SQL命令 TOP 大纲 参数 描述 TOP int值 TOP和缓存查询 TOP和ORDER BY TOP 优化 TOP与聚合和函数 示例 第七十六章 SQL命令 TOP 指定返 ...
- abbex 区块链学院如何交易之 第十六章 交易计划
第十六章 交易计划 Abbex 规则运用的铺垫没有一个交易计划,再好的规则也无从施展.怎样给自己制定完善的交易计划?幽灵没有忘记弥补这个重要的环节. 幽灵决定,把在<期货杂志>论坛发表的内 ...
- 十六届智能车全向组硬件开源 | 上海海事大学全向行进组
简 介: 卓大,不知道你能不能看到我的消息.今年是我做车的第三年了,也是最后一年了.三年里经历了许多,走了很多弯路,心里的感慨也不少.这两天在整理资料的时候顺手在CSDN上写了一篇全向组的硬件开源贴, ...
最新文章
- asp.net利用Web Service实现短信发送致手机
- js联动清除的一个想法
- VS2010集成Qt5.2环境搭建
- 【IT资讯】年薪170万码农征友,条件让网友炸锅……
- python写机器人程序_用Python写的一个多线程机器人聊天程序
- java 配置参数_给你的JAVA程序配置参数(Properties的使用)
- 关于dev无法更新、调试的问题
- [视频]Google Chrome背后的故事
- 管理项目的问题跟踪器的提示
- linux 进程0 写时复制,linux 写时复制 COW 过程梳理
- 洛谷 P4017 最大食物链计数
- python的os库的remove可以删除只带有只读属性的_扩展库os中的方法remove()可以删除带有只读属性的文件。(2.0分)_学小易找答案...
- 音频频谱特效 jaVa_基于Matlab dsp工具箱 的实时音频采集及频谱显示
- Eclipse基础--java环境变量设置
- 2022-2028全球与中国移动卫星通信市场现状及未来发展趋势
- Anaconda下载(Windows系统)
- [算法笔记]NPC问题证明sample
- cmd net use 命令
- 计算机win7启动不起来,详解电脑启动不了怎么办
- 健身房训练计划—背部