NOIp 2010 解题报告

1.   机器翻译

可以看出这是一道队列的模拟题目。按照题目要求模拟翻译过程即可。

复杂度O(N)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7
 8 //variable//
 9 int q[1010],n,m,vis[1010];
10
11 //solve//
12 int main(){
13     int ans=0;
14     scanf("%d%d",&m,&n);
15     int he=1,ta=0;
16     for (int i=1;i<=n;++i){
17         int x;
18         scanf("%d",&x);
19         if (vis[x]) continue;
20         vis[x]=1;
21         ++ans;
22         if (ta-he+1==m){
23             vis[q[he++]]=0;
24         }
25         q[++ta]=x;
26     }
27     printf("%d\n",ans);
28     return 0;
29 }

2.   乌龟棋

可以想到是动态规划,那么问题是如何定义状态。

状态定义的原则是:依据所给的状态描述,所有实际状态必须唯一。

而上文提到的实际状态的含义是:题目所给的相关量(不包括所求量)的值。比如在这一题而言,就是4种卡片的数量和到达的位置。

只有满足上述原则,才能利用局部最优推导下一个局部最优直至整体最优。即,对于这道题而言,定义状态为“棋子所在的位置”是一定不可行的,因为对于一个位置x,到达它所耗的卡片不唯一,则剩余卡片也不唯一,实际状态就不唯一,无法推出最终答案。

找到满足上述原则的状态定义实际不难。方法是:让无法通过计算得到的实际状态加入到状态描述中(即我们开的数组)。

对于这道题,我们发现可以用四个卡片的使用数来描述状态。即定义f[i][j][k][t]代表使用i张1卡片,j张2卡片,k张3卡片,t张4卡片所能得到的最大价值。

转移也就很简单了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7
 8 //variable//
 9 int n,m,a[400],tot[5],f[45][45][45][45];
10
11 //solve//
12 int main(){
13     scanf("%d%d",&n,&m);--n;
14     for (int i=0;i<=n;++i){
15         scanf("%d",a+i);
16     }
17     for (int i=1;i<=m;++i){
18         int x;
19         scanf("%d",&x);
20         ++tot[x];
21     }
22     f[0][0][0][0]=a[0];
23     for (int i=0;i<=tot[1];++i){
24         for (int j=0;j<=tot[2];++j){
25             for (int k=0;k<=tot[3];++k){
26                 for (int t=0;t<=tot[4];++t){
27                     int maxx=f[i][j][k][t],loc=i+j*2+k*3+t*4;
28                     if (i) maxx=max(maxx,f[i-1][j][k][t]+a[loc]);
29                     if (j) maxx=max(maxx,f[i][j-1][k][t]+a[loc]);
30                     if (k) maxx=max(maxx,f[i][j][k-1][t]+a[loc]);
31                     if (t) maxx=max(maxx,f[i][j][k][t-1]+a[loc]);
32                     f[i][j][k][t]=maxx;
33                 }
34             }
35         }
36     }
37     printf("%d\n",f[tot[1]][tot[2]][tot[3]][tot[4]]);
38     return 0;
39 }

3.   关押罪犯

由于他要求最大的怨气值最小,于是想到二分。

将怨气从大到小排序,二分,将二分到的前面的罪犯组两两连边,BFS染色看存不存在奇环(即能否构成二分图)。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7
 8 //type//
 9 struct rec{
10     int from,dest,cost;
11 };
12
13 //variable//
14 int n,m;
15 int last[20010],prel[200010],dest[200010],vis[20010],dep[20010],q[100000],tot=1;
16 rec g[100100];
17
18 //function prototype//
19 bool comp(rec,rec);
20 void calc_ans(void);
21 void addline(int,int);
22 bool bfs(int);
23 bool judge(int);
24
25 //solve//
26 int main(){
27     scanf("%d%d",&n,&m);
28     for (int i=1;i<=m;++i){
29         scanf("%d%d%d",&g[i].from,&g[i].dest,&g[i].cost);
30     }
31     sort(g+1,g+m+1,comp);
32     calc_ans();
33     return 0;
34 }
35
36 void calc_ans(){
37     int l=1,r=m;
38     while (l<=r){
39         int mid=l+r>>1;
40         if (judge(mid)){
41             l=mid+1;
42         }else{
43             r=mid-1;
44         }
45     }
46     printf("%d\n",g[l].cost);
47 }
48
49 bool judge(int x){
50     memset(last,0,sizeof last);tot=1;
51     for (int i=1;i<=x;++i){
52         addline(g[i].from,g[i].dest);
53         addline(g[i].dest,g[i].from);
54     }
55     memset(vis,0,sizeof vis);
56     memset(dep,0,sizeof dep);
57     bool flag=true;
58     for (int i=1;i<=n;++i){
59         if (!vis[i]){
60             bool f=bfs(i);
61             flag=(flag&&f);
62             if (!flag) break;
63         }
64     }
65     return flag;
66 }
67
68 bool bfs(int x){
69     int he=1,ta=1;
70     q[1]=x;vis[x]=1;dep[x]=0;
71     while (he<=ta){
72         int u=q[he];
73         q[he++]=0;
74         for (int k=last[u];k;k=prel[k]){
75             if (!vis[dest[k]]){
76                 vis[dest[k]]=1;
77                 dep[dest[k]]=dep[u]^1;
78                 q[++ta]=dest[k];
79             }else{
80                 if (dep[u]==dep[dest[k]]){
81                     return false;
82                 }
83             }
84         }
85     }
86     return true;
87 }
88
89 void addline(int u,int v){
90     dest[++tot]=v;
91     prel[tot]=last[u];
92     last[u]=tot;
93 }
94
95 bool comp(rec a,rec b){
96     return a.cost>b.cost;
97 }

4.   引水入城

易证:当能满足要求时,临湖一侧的点i所能引水到的区域一定是连续的区间。如果不是,则一定不满足都建有水利设施的要求。

首先将所有湖泊一侧都装上蓄水厂,BFS,判定是否无解。无解输出即可,有解进入下一步。该过程复杂度O(NM)

设湖泊一侧点i覆盖的沙漠一侧的区间为[ l[i],r[i] ],则答案变成了动态规划问题: f[i]=min{f[l[j]-1]+1,f[i]}(l[j]≤i≤r[j])。这个过程复杂度O(M2)。

下面问题是如何求l[i]和r[i]。

如果对每个湖泊侧的点BFS的话复杂度为O(NM2),有90分。

由于区间连续,我们可以从沙漠侧向湖泊侧DFS:

首先求L数组:1到m枚举DFS的起始点。DFS过程中将走过的点打上标记。如果下一个点走到了标记过的点,回溯,因为我们求L数组,一定是要找可覆盖区间的最小右标号,对于一个已经走过的点(x,y),先前已经有沙漠一侧的点j<i到达过这里,则由(x,y)能DFS到的湖泊侧点的左端点一定不大于j,则对一个i>j,再走点(x,y)也得不到更小的L。

如是优化,同理逆序循环求R,O(NM)求L和R。

至此,本题目完美解决。总复杂度O(NM+M2)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cstdio>
 5 using namespace std;
 6
 7 //constant//
 8 const int xy[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
 9
10 //type//
11 struct apair{
12     int l,r;
13 }g[520];
14
15 struct node{
16     int x,y;
17 }q[300000];
18
19 //variable//
20 int n,m,now;
21 int a[520][520],f[520];
22 bool p[520][520];
23
24 //function prototype//
25 void bfs(void);
26 void dfs(int,int);
27
28 //solve//
29 int main(){
30     scanf("%d%d",&n,&m);
31     for (int i=1;i<=n;i++)
32         for (int j=1;j<=m;j++)
33             scanf("%d",&a[i][j]);
34     bfs();
35     int rest=0;
36     for (int i=1;i<=m;i++)
37         if (!p[n][i])
38             rest++;
39     if (rest){
40         puts("0");
41         printf("%d\n",rest);
42         return 0;
43     }
44     puts("1");
45     for (int i=1;i<=m;i++){
46         memset(p,0,sizeof(p));
47         now=i;
48         g[now].l=m+1;
49         g[now].r=0;
50         dfs(1,i);
51     }
52     f[0]=0;
53     for (int i=1;i<=m;i++){
54         f[i]=1<<30;
55         for (int j=1;j<=m;j++)
56             if (i>=g[j].l&&i<=g[j].r)
57                 f[i]=min(f[i],f[g[j].l-1]+1);
58     }
59     printf("%d\n",f[m]);
60 }
61
62 void bfs(){
63     int h=0,t=0;
64     for (int i=1;i<=m;i++){
65         p[1][i]=true;
66         q[++t].x=1;
67         q[t].y=i;
68     }
69     while (h<t){
70         node u=q[++h];
71         for (int i=0;i<4;i++){
72             node v;
73             v.x=u.x+xy[i][0];
74             v.y=u.y+xy[i][1];
75             if (v.x>n||v.x<1||v.y>m||v.y<1) continue;
76             if (a[u.x][u.y]<=a[v.x][v.y]) continue;
77             if (p[v.x][v.y]) continue;
78             p[v.x][v.y]=true;
79             q[++t]=v;
80         }
81     }
82 }
83
84 void dfs(int x,int y){
85     p[x][y]=true;
86     if (x==n){
87         g[now].l=min(g[now].l,y);
88         g[now].r=max(g[now].r,y);
89     }
90     for (int i=0;i<4;i++){
91         int xx=x+xy[i][0];
92         int yy=y+xy[i][1];
93         if (xx>n||xx<1||yy>m||yy<1) continue;
94         if (a[x][y]<=a[xx][yy]) continue;
95         if (!p[xx][yy])
96             dfs(xx,yy);
97     }
98 }

时间: 2024-08-14 01:45:39

NOIp 2010 解题报告的相关文章

NOIP 2003解题报告

第一题(神经网络): 题目大意,给出一些点,每个点都有2个值,c和u,给出一些有向边,权值为w.入度为0的点的c已知,其它点的c未知,每个入度不为0的点node的c等于sum(c[k]*w[k][node]),k为与它相连(有向边指向node)的点.求所有出度为0且c>0的点. 解题过程: 此题的方法应该很多,比如拓扑排序,BFS等:我直接写了个递归,要求某个点的c,就先访问所有与它相连的点,递归下去直到 碰到入度为0的点.. 较麻烦的送分题,30分钟AC 第二题(侦探推理): 明明同学最近迷上

NOIP 2006 解题报告

第一题: 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记.因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量.如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为(Mars单位),新产生的珠子的头标记为m,尾

NOIP 2000解题报告

题目简单,思路很快就有,关键是代码实现能力,大概3个多小时完成.第一题:题目大意:将一个10进制数N转换成-B进制数 (负进制转换):B<=20, N(-32768<=N<=32767): 解题过程:首先想到仿照 正进制转换过程,但结果中有负数,就想到把负数处理掉,加个B即可,为了是结果不变,比它高1位的那一位要加1,这样某一位又有可能出现B这个数字,同理减去一个B,高位减去1:重复知道全部是0到B-1的数字:40分钟一次AC 第二题:题目大意:给定一个N位数字,用k个乘号将其划分为k+

NOIP 2001解题报告

第一题:  有形如:ax3+bx2+cx+d=0  这样的一个一元三次方程.给出该方程中各项的系数(a,b,c,d  均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1.要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位. 解题过程: 直接枚举,把根的范围扩大到100来处理 (精确到小数点后2位 ),水题,15分钟写完AC.如果精确到小数点后6位什么的,就只能二分求解了..我是把结果和0相差不到0.02的 就当成

10 noip 机器翻译 解题报告

题目描述 Description 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章. 这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义 来替换.对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有, 软件就会用它进行翻译:如果内存中没有,软件就会在外存中的词典内查找,查出单词的中 文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译. 假设内存中有M个单元,每单元能存放一个单词和译义.每当软件将一个新单词存入 内存前,如

NOIp 2013 Day2 解题报告

NOIp 2013 Day2 解题报告 1.   积木大赛 每次只要选取连续最大的一段区间即可. 继续归纳可得,答案为∑i=1nmax{0,hi-hi-1} 复杂度O(N) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable/

NOIp 2013 Day1 解题报告

NOIp 2013 Day1 解题报告 1.   转圈游戏 不难看出答案就是(x+m*10k) mod n 用快速幂算法,复杂度O(log2k) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable// 9 int n,m,x,

[noip2011]铺地毯(carpet)解题报告

最近在写noip2011的题,备战noip,先给自己加个油! 下面是noip2011的试题和自己的解题报告,希望对大家有帮助,题目1如下 1.铺地毯(carpet.cpp/c/pas) [问题描述]为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯.一共有n 张地毯,编号从1 到n.现在将这些地毯按照编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上.地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的

BZOJ 1051 最受欢迎的牛 解题报告

题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[Submit][Status][Discuss] Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎.你的任务是求出有多少头 牛被所有的牛