Noip2016 提高组 Day1
T1 玩具迷题
思路:
1.首先根据数据范围来看,储存小人的姓名开一个二维char数组即可(不会开爆)
2.然后看他给出的样例以及条件什么的,能够确定出
①朝内向右,朝外向左均为+
②朝内向左,朝外向右均为-
但是需要注意的是 加完之后超出最大数n-1(我从0开始进行计数),所以需要进行-n
减完之后小于0的话,需要进行+n
3.使问题更简单的方式 (y是移动步数)
- if(y>n)
- y=y%(n+);
(不懂的话可以手动模拟一下)
上代码:
- #include <iostream>
- #include <cstdio>
- using namespace std;
- const int Maxn = ;
- const int Maxl = ;
- int n,m,len;
- bool f[Maxn];
- char ssr[Maxn][Maxl];
- int main() {
- scanf("%d%d",&n,&m);
- string sr;
- for(int i=; i<n; ++i) {
- scanf("%d",&f[i]);
- cin>>sr;
- len=sr.length();
- for(int j=; j<len; ++j)
- ssr[i][j]=sr[j];
- }
- int nxt=;
- for(int i=,x,y; i<m; ++i) {
- scanf("%d%d",&x,&y);
- if(y>n)
- y=y%(n+);
- if(x) {//right
- if(f[nxt]==) //朝内
- nxt+=y;
- else //朝外
- nxt-=y;
- }
- else {//left
- if(f[nxt]==) //朝内
- nxt-=y;
- else //朝外
- nxt+=y;
- }
- if(nxt<)
- nxt+=n;
- if(nxt>=n)
- nxt-=n;
- }
- for(int i=; ; ++i) {
- if(ssr[nxt][i])
- printf("%c",ssr[nxt][i]);
- else
- break;
- }
- return ;
- }
T2 天天爱跑步
思路:
首先看到他给出这么详细的数据范围以及约定,不打一下暴力简直就是对不起他啊!所以首先我们这道题可以打暴力!
①首先来看前4个点中的前两个点的约定是起点等于终点,他的意思就是说明每一个点只存在于0秒的时候。
而另外两个点的约定是所有的Wj均为0,所以这四个点再输入u,v的时候不用存边,只需要把起点终点以及每个点的Wj即可。
具体的方法就是从1到m循环一遍若当前点的times为0,ans[该点的初始时间]++,最后按顺序输出ans数组即可
核心代码如下:
- if(flag1||flag2) { //所有点的起点与终点相同 || 所有的Wj==0
- for(int i=,u,v; i<n; ++i)
- scanf("%d%d",&u,&v);
- for(int i=; i<=n; ++i)
- scanf("%d",×[i]);
- for(int i=; i<=m; ++i)
- scanf("%d%d",&t[i].si,&t[i].ti);
- for(int i=; i<=m; ++i) {
- if(!times[t[i].si])
- ans[t[i].si]++;
- }
- for(int i=; i<=n; ++i)
- printf("%d ",ans[i]);
- return ;
- }
②然后我们来看第五个点,没有任何约定!但是看看数据范围跟前四个是一样的。
等等!如果说数据范围的话来。。。。这个数据范围辣么小,眼前一亮!其实这不就是个搜索嘛!
(这样子就可以将前五个点连接起来写一个解题方法了!)
搜索思路:
对于题目中给出的m个人的起止点进行dfs,记录下来他当前花费的时间以及他当前的父节点(以便于寻找并更新他的子节点)
又因为在dfs中具有回退过程,若已经找到了正确的路线(即找到了终点now==end)后,
可能还会再次进行搜索另外一些没有被搜索过,但是没有什么用的点(其实说白了就是在浪费时间),
所以我们需要在回退过程中在进行更新p数组
核心代码如下:(亲测搜索比特判还要快qwq,差不多快100多ms)
- void dfs(int now,int ed,int time,int pre) {
- if(now==ed) {
- ok=true;
- p[now][time]++;
- return ;
- }
- if(ok) return ; //防止找到正确的路线之后在回退的过程中再次进行搜索一些无用的点,所以直接return
- for(int i=head[now],v; i; i=e[i].next) {
- v=e[i].to;
- if(v==pre) continue; //不是子节点
- dfs(v,ed,time+,now); //更新now,time,pre并继续搜索下去
- if(ok)
- {
- p[now][time]++; //将正确的路线上的点的p进行更新
- return;
- }
- }
- }
③6.7.8个点:如果一棵树树退化成一条链的话,那么这个点上的观察员,
就只能够看到从i+-times[i]这两个位置出发的人的就只需要进行加减再加上特判他存在即可
我们需要知道:
如果i能看到起点深度比他小的点,那么这个点的终点需要>=i
如果i能看到起点深度比他大的点,那么这个点的终点需要<=i
核心代码如下:
- for(int i=; i<=n; ++i) {
- if(i-times[i]>) //若往前找有可能能够观察到人
- for(int j=head[i-times[i]],v; j; j=e[j].next) {
- v=e[j].to; //能够成功被观察到的这个点的编号为v(通过链表进行寻找)
- if(i<=t[v].ti) //是从v点往后找,所以v点的目的地必须要大于i才可以,不然走不到i点
- ans[i]++;
- }
- if(i+times[i]<=n) { //若往后找有可能能够观察到人
- for(int j=head[i+times[i]],v; j; j=e[j].next) {
- v=e[j].to;
- if(i>=t[v].ti) //同理,因为上一种情况是从v点往后找,所以v点的目的地必须大于i,这一种情况则相反(小于)
- ans[i]++;
- }
- }
- }
④9-12点:所有的点的起点相同!
我们若设1号的深度是0,那么只有当那些观察员们所在节点的深度与当前这个节点的Wi是相同的,他才有可能能够观察到人。
若不相等的话,直接输出0即可
若相等的话,那么他就一定能够观察到所有经过他的人,也就是说以i为根的子树中拥有终点的个数有多少个
所以首先可以bfs搜索出所有点的深度然后用一个简单的dfs来统计每一个节点的子树中拥有终点的个数(用son数组记录下来)
核心代码如下:
- void bfs() {
- queue<Flag5>q;
- cur.dep=; cur.id=; dad[]=;
- q.push(cur);
- while(!q.empty()) {
- cur=q.front();
- q.pop();
- int u=cur.id;
- for(int i=head[cur.id],v; i; i=e[i].next) {
- v=e[i].to;
- if(v==dad[u]) continue;
- dad[v]=u; nxt.dep=cur.dep+;
- nxt.id=v; deeps[v]=deeps[u]+;
- q.push(nxt);
- }
- }
- }
- void Dfs(int u) {
- son[u]=sum[u];
- for(int i=head[u],v; i; i=e[i].next) {
- v=e[i].to;
- if(v==dad[u]) continue;
- Dfs(v);
- son[u]+=son[v];
- }
- }
至于正解什么的,待研究中。。。
神奇的第一反映233:
天天就是天天。
上代码:
①暴力代码小汇总
- #include <iostream>
- #include <cstdio>
- #include <cmath>
- #include <queue>
- using namespace std;
- const int N = ;
- const int Maxn = ;
- int n,m;
- int times[Maxn],ans[Maxn];
- struct B {
- int next,to;
- }e[Maxn<<];
- int top,head[Maxn];
- inline void add(int u,int v) {
- top++;e[top].to=v;
- e[top].next=head[u];head[u]=top;
- }
- bool flag,ok,flag1,flag2,flag4,flag5;
- int p[N][N],sum[Maxn],deeps[Maxn],dad[Maxn],son[Maxn];
- struct Flag4 {
- int si,ti;
- }t[Maxn];
- struct Flag5 {
- int dep,id;
- }cur,nxt;
- void dfs(int now,int ed,int time,int pre) {
- if(now==ed) {
- ok=true;
- p[now][time]++;
- return ;
- }
- if(ok) return ; //防止找到正确的路线之后在回退的过程中再次进行搜索一些无用的点,所以直接return
- for(int i=head[now],v; i; i=e[i].next) {
- v=e[i].to;
- if(v==pre) continue; //不是子节点
- dfs(v,ed,time+,now); //更新now,time,pre并继续搜索下去
- if(ok)
- {
- p[now][time]++; //将正确的路线上的点的p进行更新
- return;
- }
- }
- }
- void bfs() {
- queue<Flag5>q;
- cur.dep=; cur.id=; dad[]=;
- q.push(cur);
- while(!q.empty()) {
- cur=q.front();
- q.pop();
- int u=cur.id;
- for(int i=head[cur.id],v; i; i=e[i].next) {
- v=e[i].to;
- if(v==dad[u]) continue;
- dad[v]=u; nxt.dep=cur.dep+;
- nxt.id=v; deeps[v]=deeps[u]+;
- q.push(nxt);
- }
- }
- }
- void Dfs(int u) {
- son[u]=sum[u];
- for(int i=head[u],v; i; i=e[i].next) {
- v=e[i].to;
- if(v==dad[u]) continue;
- Dfs(v);
- son[u]+=son[v];
- }
- }
- int main() {
- freopen("runninga.in","r",stdin);
- freopen("runninga.out","w",stdout);
- scanf("%d%d",&n,&m);
- if(n%<=) flag=true;
- // if(n%10==1) flag1=true;
- // if(n%10==2) flag2=true;
- if(n%==) flag4=true;
- if(n%==) flag5=true;
- if(flag) {
- for(int i=,u,v; i<n; ++i) {
- scanf("%d%d",&u,&v);
- add(u,v),add(v,u);
- }
- for(int i=; i<=n; ++i)
- scanf("%d",×[i]);
- for(int i=,u,v; i<=m; ++i) {
- scanf("%d%d",&u,&v);
- ok=false;
- dfs(u,v,,);
- }
- for(int i=; i<=n; ++i)
- printf("%d ",p[i][times[i]]);
- return ;
- }
- /*
- if(flag1||flag2) { //所有点的起点与终点相同 || 所有的Wj==0
- for(int i=1,u,v; i<n; ++i)
- scanf("%d%d",&u,&v);
- for(int i=1; i<=n; ++i)
- scanf("%d",×[i]);
- for(int i=1; i<=m; ++i)
- scanf("%d%d",&t[i].si,&t[i].ti);
- for(int i=1; i<=m; ++i) {
- if(!times[t[i].si])
- ans[t[i].si]++;
- }
- for(int i=1; i<=n; ++i)
- printf("%d ",ans[i]);
- return 0;
- }
- */
- if(flag4) { //关系退化为一条链
- for(int i=,u,v; i<n; ++i)
- scanf("%d%d",&u,&v);
- for(int i=; i<=n; ++i)
- scanf("%d",×[i]);
- for(int i=; i<=m; ++i) {
- scanf("%d%d",&t[i].si,&t[i].ti);
- add(t[i].si,i); //将该点的初始时间与该点的编号进行连接
- }
- for(int i=; i<=n; ++i) {
- if(i-times[i]>) //若往前找有可能能够观察到人
- for(int j=head[i-times[i]],v; j; j=e[j].next) {
- v=e[j].to; //能够成功被观察到的这个点的编号为v(通过链表进行寻找)
- if(i<=t[v].ti) //是从v点往后找,所以v点的目的地必须要大于i才可以,不然走不到i点
- ans[i]++;
- }
- if(i+times[i]<=n) { //若往后找有可能能够观察到人
- for(int j=head[i+times[i]],v; j; j=e[j].next) {
- v=e[j].to;
- if(i>=t[v].ti) //同理,因为上一种情况是从v点往后找,所以v点的目的地必须大于i,这一种情况则相反(小于)
- ans[i]++;
- }
- }
- }
- for(int i=; i<=n; ++i)
- printf("%d ",ans[i]);
- return ;
- }
- if(flag5) { //所有人的起点均相同,且为1
- for(int i=,u,v; i<n; ++i) {
- scanf("%d%d",&u,&v);
- add(u,v),add(v,u);
- }
- for(int i=; i<=n; ++i)
- scanf("%d",×[i]);
- for(int i=,u,v; i<=m; ++i) {
- scanf("%d%d",&u,&v);
- sum[v]++;
- }
- bfs();
- Dfs();
- for(int i=; i<=n; ++i)
- if(times[i]==deeps[i])
- printf("%d ",son[i]);
- else
- printf("0 ");
- }
- return ;
- }
②咦?正解呢?嘻嘻,还不会啦!
T3 换教室
思路:
这是一道概率DP的题
首先我们用floyd求两教室间最短路,这个不用说,三个for循环即可
然后我们可以用f[i,j,k] 表示前i个时间段申报j个。
其中:k=0表示当前时间段没选,k=1表示当前时间段选了
上代码:
- #include <iostream>
- #include <cstdio>
- #include <cmath>
- #include <cstring>
- #include <algorithm>
- #define INF 0x7fffffff
- using namespace std;
- const int Mn = ;
- const int Mv = ;
- int n,m,v,e;
- int c[Mn],d[Mn],map[Mv][Mv];
- double ans=20020811.233,tmp;
- double k[Mn],f[Mn][Mn][];
- //f[i][j][k]表示前i个时间段申报j个.
- //k=0表示当前时间段没选,k=1表示当前时间段选了
- void pre() {
- //这个初始化有点坑说实话...
- memset(map,0x3f,sizeof(map));
- for(int i=; i<Mv; ++i) map[i][i]=;
- /*--------------------------------*/
- for(int i=; i<Mn; ++i)
- for(int j=; j<Mn; ++j)
- f[i][j][]=f[i][j][]=INF;
- }
- void floyd() {
- for(int k=; k<=v; ++k)
- for(int i=; i<=v; ++i)
- for(int j=; j<=v; ++j)
- if(map[i][k]+map[k][j]<map[i][j])
- map[i][j]=map[i][k]+map[k][j];
- }
- void dp() {
- f[][][]=f[][][]=;
- for(int i=; i<=n; ++i) { //从第二天开始换教室
- for(int j=; j<=i && j<=m; ++j) { //开始dp选取换第几个教室
- double tmp0=f[i][j][];
- f[i][j][]=
- min(f[i-][j][]+map[c[i-]][c[i]], //上一天不换并且今天也不换,所以直接加上两教室之间的距离(直接走过去)即可
- f[i-][j][] //上一天换
- +map[c[i-]][c[i]]*(1.0-k[i-]) //申报了但是申请不成功的概率
- +map[d[i-]][c[i]]*k[i-]); //申报了并且申请成功的概率
- f[i][j][]=min(tmp0,f[i][j][]); //跟没有更新之前的进行比较取最优值
- if(j) { //如果能申报
- double tmp1=f[i][j][];
- f[i][j][]=
- min(f[i-][j-][] //前i-1天申报j-1(因为当天已经确定申报)次
- +k[i]*map[c[i-]][d[i]] //当天申报成功
- +(1.0-k[i])*map[c[i-]][c[i]], //当天申报不成功
- f[i-][j-][] //同上
- +(1.0-k[i-])*k[i]*map[c[i-]][d[i]] //当天申报成功,上一天申报不成功
- +k[i-]*k[i]*map[d[i-]][d[i]] //当天申报成功,上一天申报也成功
- +k[i-]*(1.0-k[i])*map[d[i-]][c[i]] //当天申报不成功,上一天申报成功
- +(1.0-k[i-])*(1.0-k[i])*map[c[i-]][c[i]] //当天申报不成功,上一天申报也不成功
- );
- f[i][j][]=min(f[i][j][],tmp1); //跟没有更新之前的进行比较取最优值
- }
- }
- }
- }
- int main() {
- // freopen("classrooma.in","r",stdin);
- // freopen("classrooma.out","w",stdout);
- scanf("%d%d%d%d",&n,&m,&v,&e);
- for(int i=; i<=n; ++i) scanf("%d",&c[i]);
- for(int i=; i<=n; ++i) scanf("%d",&d[i]);
- for(int i=; i<=n; ++i) scanf("%lf",&k[i]);
- pre();
- for(int i=,a,b,w; i<=e; ++i) {
- scanf("%d%d%d",&a,&b,&w);
- if(w<map[a][b]) map[a][b]=map[b][a]=w;
- }
- floyd();
- dp();
- for(int i=; i<=m; ++i)
- tmp=min(f[n][i][],f[n][i][]),ans=min(ans,tmp);
- printf("%.2lf\n",ans);
- return ;
- }
Noip2016 提高组 Day1的更多相关文章
- 【NOIP2016提高组day1】换教室
题目 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的 课程. 在可以选择的课程中,有 2n 节课程安排在 n 个时间段上. 在第 i ( 1 ≤ i ≤ n )个 时间段上,两 ...
- NOIP2016提高组解题报告
NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合
- 【题解】NOIP2016提高组 复赛
[题解]NOIP2016提高组 复赛 传送门: 玩具谜题 \(\text{[P1563]}\) 天天爱跑步 \(\text{[P1600]}\) 换教室 \(\text{[P1850]}\) 组合数问 ...
- luogu1003铺地毯[noip2011 提高组 Day1 T1]
题目描述 为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯.一共有 n 张地毯,编号从 1 到n .现在将这些地毯按照编号从小到大的顺序平行于 ...
- 18/9/9牛客网提高组Day1
牛客网提高组Day1 T1 中位数 这好像是主席树??听说过,不会啊... 最后只打了个暴力,可能是n2logn? 只过了前30% qwq #include<algorithm> #in ...
- Noip2011 提高组 Day1 T1 铺地毯 + Day2 T1 计算系数
Day1 T1 题目描述 为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯.一共有 n 张地毯,编号从 1 到n .现在将这些地毯按照编号从小 ...
- Noip2011 提高组 Day1 T3 Mayan游戏
题目描述 Mayan puzzle是最近流行起来的一个游戏.游戏界面是一个 7 行5 列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上.游戏通关是指在规定 ...
- 【题解】NOIP2016 提高组 简要题解
[题解]NOIP2016 提高组 简要题解 玩具迷题(送分) 用异或实现 //@winlere #include<iostream> #include<cstdio> #inc ...
- GZOJ 1361. 国王游戏【NOIP2012提高组DAY1】
国王游戏[NOIP2012提高组DAY1] Time Limit:1000MS Memory Limit:128000K Description 国王游戏(game.cpp/c/pas) [问题描述] ...
随机推荐
- PostgreSQL练习
学生表 Studentcreate table Student(Sid varchar(6), Sname varchar(10), Sage datetime, Ssex varchar(10)); ...
- c++ vector容器
https://www.runoob.com/w3cnote/cpp-vector-container-analysis.html
- RabbitMQ安装&简单使用
.什么是RabbitMQ.详见 http://www.rabbitmq.com/. 作用就是提高系统的并发性,将一些不需要及时响应客户端且占用较多资源的操作,放入队列,再由另外一个线程,去异步处理这些 ...
- C# 读取本地图片
/// <summary> /// 通过FileStream 来打开文件,这样就可以实现不锁定Image文件,到时可以让多用户同时访问Image文件 /// </summary> ...
- Unity Cube一面显示图片
Cube加plane 把plane调整到和cube的一面一样大小,并放到那一面的位置,然后再Hierarchy面板选中plane,把图片拖到Inspector的plane下.
- css文本超出隐藏 显示三个点
文本超出显示三个点一般分两种情况 一,单行文本超出隐藏 overflow:hidden; text-overflow:ellipsis; white-space:nowrap; 二,多行文本超出隐藏 ...
- python打印菱形
1.分析:首先python,我们分析了菱形的成分.双喜鸟seo输入2时,打印三行菱形:输入3时,打印五行菱形.也就是说,根据输入数字A,打印第2a-1行的菱形.菱形由一个三角形和一个倒三角形组成,两个 ...
- 请求上下文HttpContext解释
1 HttpContext上下文作用 有关应用程序状态信息,处理的请求以及构建的响应等信息全部通过HttpContext上下文获取 2 Httpcontext类用于从头至尾跟踪请求的状态,他也是有关请 ...
- footer始终在页面最底部的方法(问题待检验)
一.css方法 <style type="text/css"> html,body{ height: 100%; } body{ display: flex; flex ...
- 解决WinForm屏幕缩放适配只需修改两个Form的两个属性
最近要做一个windows下截屏识别文字的程序,调试发现截取的图像显示不完整. 输出了Screen.PrimaryScreen.Bounds.Width获取的值,结果与实际分辨率不同,所以确定了与我的 ...