Noip2010提高组总结
将Noip2010重新做了一遍,第一遍做下来居然只有290分,比当年浙江的一等线低了20分,因为各种坏习惯丢掉了许多分数,Noip时需要特别注意!
T1:机器翻译
第一题直接暴力,内存足够所以不用循环队列,5分钟AC:
#include <cstdio>
int stack[10000],now;
int main(){
int m,n;
scanf("%d%d",&m,&n); int head=m;
for(int i=0;i<m;i++)stack[i]=-1;
for(int i=0;i<n;i++){
bool flag=true;
scanf("%d",&now);
for(int j=head-m;j<head;j++)if(stack[j]==now){flag=false;break;}
if(flag)stack[head++]=now;
}
printf("%d\n",head-m);
return 0;
}
T2:乌龟棋
对于这道题目直接以四种卡片为状态进行四维Dp就可以了,注意DP时加上的是格子1*i+2*j+3*k+4*l的数值,而不是i+j+k+l的,一开始出错调试时都过不了,修改好AC:
#include <cstdio>
int t[5],a[40000],s[5];
int f[41][41][41][41];
int main(){
int m,n,in,ans=0;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
for(int i=0;i<m;i++)scanf("%d",&in),s[in]++;
f[0][0][0][0]=a[0];
for(int i=0;i<=s[1];i++)
for(int j=0;j<=s[2];j++)
for(int k=0;k<=s[3];k++)
for(int l=0;l<=s[4];l++){
if(f[i+1][j][k][l]<f[i][j][k][l]+a[1*i+2*j+3*k+4*l+1])
f[i+1][j][k][l]=f[i][j][k][l]+a[1*i+2*j+3*k+4*l+1];
if(f[i][j+1][k][l]<f[i][j][k][l]+a[1*i+2*j+3*k+4*l+2])
f[i][j+1][k][l]=f[i][j][k][l]+a[1*i+2*j+3*k+4*l+2];
if(f[i][j][k+1][l]<f[i][j][k][l]+a[1*i+2*j+3*k+4*l+3])
f[i][j][k+1][l]=f[i][j][k][l]+a[1*i+2*j+3*k+4*l+3];
if(f[i][j][k][l+1]<f[i][j][k][l]+a[1*i+2*j+3*k+4*l+4])
f[i][j][k][l+1]=f[i][j][k][l]+a[1*i+2*j+3*k+4*l+4];
}
for(int i=0;i<=s[1];i++)
for(int j=0;j<=s[2];j++)
for(int k=0;k<=s[3];k++)
for(int l=0;l<=s[4];l++){
if(f[i][j][k][l]>ans&&(i+2*j+3*k+4*l+1==n))ans=f[i][j][k][l];
}
printf("%d\n",ans);
return 0;
}
T3:关押罪犯
属于最大值最小问题,可以用二分答案,BFS检验一下,但是我直接用的是并查集,感觉就是先排序,然后按边权由大到小进行并查集,关在同一个地方的建1边,关在不同地方的建0边,当然,对于数据全都建1边,在传递时01交替即可,一开始因为懒,没有码余数体系,直接敲了异或,没考虑全面,只拿了六十分,后来还是老老实实地写了下余数体系,AC~
#include <cstdio>
#include <algorithm>
using namespace std;
int f[20005],r[20005];
struct seg{
int a,b,l;
friend bool operator <(const seg&a,const seg&b){return a.l>b.l;}
}s[100005];
int sf(int x){
if (f[x]==x) return x;
int fx=sf(f[x]);
r[x]=(r[x]+r[f[x]])%2;
return f[x]=fx;
}
void Union(int x,int y,int fx,int fy,int d)
{
f[fy]=fx;
r[fy]=(2-r[y]+d+r[x])%2;
}
int main(){
int m,n;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)scanf("%d%d%d",&s[i].a,&s[i].b,&s[i].l);
sort(s,s+m);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=0;i<m;i++){
int fa=sf(s[i].a),fb=sf(s[i].b);
if(fa==fb){
if(r[s[i].a]==r[s[i].b]){
printf("%d",s[i].l);
return 0;
}
}
else Union(s[i].a,s[i].b,fa,fb,1);
}
puts("0");
return 0;
}
T4:引水入城
最后一题题目还是不错的,可惜推出了大半的结论,却因为代码的原因只拿了30分,特别反省一下,首先看到题目,想到floodfill,对于第一排的每一个点可以进行一次种子填充法,然后就可以知道能够填到的地方,但是这样子复杂度过高,那么是不是所有的点都要填一遍呢?
观察上面这张图,我们可以发现,如果第一排要填充的点比周围的点低,那么在填充那个点的时候就可以直接填到这个点,即一定比填充此点要优,所以在填充时,只需要填比两边点都要大或相等的点,即上图中的8,6,4,那么填充之后得到的数据该怎么处理呢?
对于每次填充其实都生成了一条线段,注意线段计算时只取第一段连续的可灌溉点,如果是不连续的,必然无解,因为水断点如果可以被另一条线段覆盖,则一定比当前优,因为其必然可以灌溉到当前线段所能灌到的所有点。
最后是求最少线段覆盖的典型贪心问题了,一开始我以为O(n)可以解决,结果发现是错的,每次取有交集的最长线段必须要扫一遍才可以,否则不一定是最优的,贴上错误代码,警示一下!
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int map[505][505],m,n;
int b[505][505];
struct seg{int a,b;}s[505];
bool cmp(seg x,seg y){
if(x.a==y.a)return x.b>y.b;
return x.a<y.a;
}
void floodfill(int x,int y){
b[x][y]=1;
if(x+1<=n&&map[x+1][y]<map[x][y]&&!b[x+1][y])floodfill(x+1,y);
if(x-1>0&&map[x-1][y]<map[x][y]&&!b[x-1][y])floodfill(x-1,y);
if(y+1<=m&&map[x][y+1]<map[x][y]&&!b[x][y+1])floodfill(x,y+1);
if(y-1>0&&map[x][y-1]<map[x][y]&&!b[x][y-1])floodfill(x,y-1);
}
int main(){
freopen("flow.in","r",stdin);
freopen("flow.out","w",stdout);
int i,j,top=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&map[i][j]);
for(i=1;i<=m;i++)if(map[1][i]>=map[1][i+1]&&map[1][i]>=map[1][i-1]){
memset(b,0,sizeof b);
floodfill(1,i);
for(j=1;j<=m;j++)if(b[n][j]){s[top].a=j;break;}
if(s[top].a==0)continue;
for(j=s[top].a;;j++)if(!b[n][j]){s[top++].b=j-1;break;}
}
sort(s,s+top,cmp);
int l=s[0].a,r=s[0].b,tmp=1;
for(j=1;j<top;j++)if((s[j].a-1)<=r&&s[j].b>r){r=s[j].b;tmp++;}
if(r==m&&l==1){puts("1");printf("%d\n",tmp);}
else{int tmp=0;
for(i=1;i<=m;i++)
if(map[1][i]>=map[1][i+1]&&map[1][i]>=map[1][i-1])floodfill(1,i);
for(j=1;j<=m;j++)if(!b[n][j])tmp++;
puts("0");printf("%d\n",tmp);
}
return 0;
fclose(stdin);fclose(stdout);
}
然后是AC程序
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int map[505][505],m,n;
int b[505][505];
struct seg{int a,b;}s[505];
bool cmp(seg x,seg y){
if(x.a==y.a)return x.b>y.b;
return x.a<y.a;
}
void floodfill(int x,int y){
b[x][y]=1;
if(x+1<=n&&map[x+1][y]<map[x][y]&&!b[x+1][y])floodfill(x+1,y);
if(x-1>0&&map[x-1][y]<map[x][y]&&!b[x-1][y])floodfill(x-1,y);
if(y+1<=m&&map[x][y+1]<map[x][y]&&!b[x][y+1])floodfill(x,y+1);
if(y-1>0&&map[x][y-1]<map[x][y]&&!b[x][y-1])floodfill(x,y-1);
}
int main(){
int i,j,top=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&map[i][j]);
for(i=1;i<=m;i++)if(map[1][i]>=map[1][i+1]&&map[1][i]>=map[1][i-1]){
memset(b,0,sizeof b);
floodfill(1,i);
for(j=1;j<=m;j++)if(b[n][j]){s[top].a=j;break;}
if(s[top].a==0)continue;
for(j=s[top].a;;j++)if(!b[n][j]){s[top++].b=j-1;break;}
}
sort(s,s+top,cmp);
int l=1,r=0,tmp=0; bool flag;
do{
int max=0; flag=1;
for(int i=0;i<top;i++)
if(s[i].a-1<=r&&s[i].b>r){if(s[i].b>max)max=s[i].b;flag=0;}
r=max; tmp++;
}while(r!=m&&!flag);
if(r==m){puts("1");printf("%d\n",tmp);}
else{int tmp=0;
for(i=1;i<=m;i++)
if(map[1][i]>=map[1][i+1]&&map[1][i]>=map[1][i-1])floodfill(1,i);
for(j=1;j<=m;j++)if(!b[n][j])tmp++;
puts("0");printf("%d\n",tmp);
}
return 0;
}
注意点:
1.不要忘记return 0;
2.提交时while(1)记得要去掉!!!
3.注意Noip时一定要开cstring,iostream不可以代替cstring!
4.注意判断用双等号! 白丢70分的教训啊~
5.加权并查集不可以用异或,100降为60分得出的结论
6.一定要多造数据来检测自己的程序!
Noip2010提高组总结的更多相关文章
- NOIP2010提高组乌龟棋 -SilverN
题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起 ...
- noip2010提高组题解
NOIP2010提高组题解 T1:机器翻译 题目大意:顺序输入n个数,有一个队列容量为m,遇到未出现元素入队,求入队次数. AC做法:直接开1000的队列模拟过程. T2:乌龟棋 题目大意:有长度为n ...
- NOIP2010提高组真题部分整理(没有关押罪犯)
目录 \(NOIP2010\)提高组真题部分整理 \(T1\)机器翻译: 题目背景: 题目描述: 输入输出格式: 输入输出样例: 说明: 题解: 代码: \(T2\)乌龟棋 题目背景: 题目描述: 输 ...
- 洛谷 P1541 乌龟棋 & [NOIP2010提高组](dp)
传送门 解题思路 一道裸的dp. 用dp[i][j][k][kk]表示用i个1步,j个2步,k个3步,kk个4步所获得的最大价值,然后状态转移方程就要分情况讨论了(详见代码) 然后就是一开始统计一下几 ...
- 洛谷 P1525 关押罪犯 & [NOIP2010提高组](贪心,种类并查集)
传送门 解题思路 很显然,为了让最大值最小,肯定就是从大到小枚举,让他们分在两个监狱中,第一个不符合的就是答案. 怎样判断是否在一个监狱中呢? 很显然,就是用种类并查集. 种类并查集的讲解——团伙(很 ...
- NOIP2010提高组 关押罪犯 -SilverN
(洛谷P1525) 题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用“怨气值”( ...
- [NOIP2010] 提高组 洛谷P1525 关押罪犯
刚才做并查集想到了这道以前做的题,干脆一并放上来 题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可 ...
- NOIP2010提高组] CODEVS 1069 关押罪犯(并查集)
这道这么简单的题目还写了这么久.. 将每个会发生冲突的两人的怒气进行排序,然后从怒气大到小,将两个人放到不同监狱中.假如两人都已经被放置且在同一监狱,这就是答案. ------------------ ...
- noip2010提高组3题题解 by rLq
本题地址http://www.luogu.org/problem/show?pid=1525 关押罪犯 题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和 ...
随机推荐
- Objective-C中一些 值得程序员注意的地方(转载)
1.有关于BOOL陷井方面有如下方面: 关于BOOL条件语句中的比较最好是与NO的值来进行比较,因为BOOL的YES与NO值只是约定,并且编译器将BOOL认作8位二进制数据.若是不小心将一个长于1字节 ...
- javascript封装自定义滚动条方法,可自定义四个边框滚动条
还是根据我的个人习惯封装了一个方法 setScroll({ box :父盒子DOM对象, content : 内容盒子DOM对象, scrollall : 滚动条大盒子DOM对象, scroll : ...
- IOS基础:深入理解Objective-c中@class的含义
objective-c中,当一个类使用到另一个类时,并且在类的头文件中需要创建被引用的指针时, 如下面代码: A.h文件 #import "B.h" @interface A : ...
- Hadoop经典面试题(转)
单项选择题 1. 下面哪个程序负责 HDFS 数据存储. a)NameNode b)Jobtracker c)Datanode d)secondaryNameNode e)tasktracker 2. ...
- Hive常用操作之数据导入导出
一.Hive数据导入导出 1.hive数据导出 很多时候,我们在hive中执行select语句,希望将最终的结果保存到本地文件或者保存到hdfs系统中或者保存到一个新的表中,hive提供了方便的关键词 ...
- nodejs安装指定版本
由于express有各种不同的版本,不同的版本开发方式有所不同,如果想安装指定版本可以选择全局安装指定版本: 安装步骤如下: (1) 安装2.5.8版本的express的方法: C:\Users\Li ...
- SSD常见问题的技术分析
AHCI对性能的影响 AHCI,全称Advanced Host Controller Interface,即高级主机控制器接口,是一种相比老旧的“IDE虚拟模式”更适合新一代SATA存储设备通信的协议 ...
- 我的MYSQL学习心得 mysql日志
这一篇<我的MYSQL学习心得(十五)>将会讲解MYSQL的日志 MYSQL里的日志主要分为4类,使用这些日志文件,可以查看MYSQL内部发生的事情. 分别是 1.错误日志:记录mysql ...
- c++中string::function集合
string append() 1.直接添加另一个完整的字符串: str1.append(str2); 2.添加另一个字符串的某一段字串: str1.append(str2, 11, 7); ...
- SQL SERVER 2008 R2 自动备份并删除过期备份数据
我们的系统维护的过程中肯定需要对数据库进行定期的备份,但是如果定时手工备份的话,不但浪费时间,也不能保证每次都可以按时备份,所以自动备份成为了我们的不二选择,但是定时备份需要定期清理备份文件, ...