20170218 机房『NOIP2010』
科学家不是依赖于个人的思想,而是综合了几千人的智慧,所有的人想一个问题,并且每人做它的部分工作,添加到正建立起来的伟大知识大厦之中。
—— 卢瑟福
我热爱生活,我是一名快速成长的OIer
目录:
T1 translate
洛谷P1540 NOIP2010 机器翻译
这是一道大水题,就不贴题了。模拟,使用循环队列求解。
但需要注意的是,单词为非负数,可能为0,需要区别它与空栈。班上很多人为此WA了几个点。
/*
User name: Doggu
Time:O(n)
Space:O(n)
*/
#define NAME "translate"
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int OO = 0x3f3f3f3f;
struct IO {
IO() {
freopen( NAME".in", "r", stdin );
freopen( NAME".out", "w", stdout );
}
~IO() {
fclose( stdin );
fclose( stdout );
}
}Doggu;
template<class TE> inline void readin(TE &res) {
static char ch;
while((ch=getchar())<'0'||ch>'9');
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-'0';
}
const int N = 1000 + 10;
const int M = 100 + 10;
int m, n, x, team[M], tot, vis[N], rear;
int main() {
memset( team, -1, sizeof(team) );
memset( vis, 0, sizeof(vis) );
readin(m);
readin(n);
tot = 0;
rear = 0;
for( int i = 1; i <= n; i++ ) {
readin(x);
if( vis[x] ) continue;
tot++;
rear = ( rear + 1 ) % m;
if( team[rear] != -1 )
vis[team[rear]] = 0;
vis[team[rear] = x] = 1;
}
printf( "%d\n", tot );
return 0;
}
T2 tortoise
洛谷P1541 NOIP2010 乌龟棋
简单DP,按题模拟,是有套路的。然而,DP什么呢?
开始时,我已经蒙了,甚至试图使用状压,但其实根本不需要。因为卡牌只有四类,且用完以后刚好就能到终点。
于是,我改变了策略,但还是想得很复杂,企图把距离DP进去。虽然可以,但转移方程很不简明,复杂度较高且易错,于是最终WA完了。
其实只需要DP四种牌的数目就够了。
#define NAME "tortoise"
#define res( x, y ) for( x = 0; x <= y; x++ )
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int OO = 0x3f3f3f3f;
struct IO {
IO() {
freopen( NAME".in", "r", stdin );
freopen( NAME".out", "w", stdout );
}
~IO() {
fclose( stdin );
fclose( stdout );
}
}Doggu;
template<class TE> inline void readin(TE &res) {
static char ch;
while((ch=getchar())<'0'||ch>'9');
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-'0';
}
const int N = 350 + 10;
const int M = 120 + 10;
const int K = 40 + 1;
int n, m, x, y, z, w, aa[N], b[5];
int f[K][K][K][K];//DP使用牌的数目
int main() {
readin(n);
readin(m);
for( int i = 1; i <= n; i++ )
readin(aa[i]);
for( int j = 1; j <= m; j++ ) {
readin(x);
b[x]++;
}
res(x,b[1])res(y,b[2])res(z,b[3])res(w,b[4]) {
int M = 0;
if(x-1>=0) M = max(M,f[x-1][y][z][w]);
if(y-1>=0) M = max(M,f[x][y-1][z][w]);
if(z-1>=0) M = max(M,f[x][y][z-1][w]);
if(w-1>=0) M = max(M,f[x][y][z][w-1]);
f[x][y][z][w] = M + aa[1+x+2*y+3*z+4*w];
}
printf( "%d\n", f[b[1]][b[2]][b[3]][b[4]] );
return 0;
}
T3 prison
洛谷P1525 NOIP2010 关押罪犯
此题贴两解。
法1:二分答案+DFS(二分图染色)
O(nlogn)
我在做此题时,DFS内部逻辑关系没有理清楚,为此WA完了。
在二分答案时,用DFS遍历一遍图,若边权无伤大雅则忽略。然后,若颜色相同则返回false,若未染色则进行拓展。
#define NAME "prison"
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int OO = 0x3f3f3f3f;
struct IO {
IO() {
freopen( NAME".in", "r", stdin );
freopen( NAME".out", "w", stdout );
}
~IO() {
fclose( stdin );
fclose( stdout );
}
}Doggu;
template<class TE> inline void readin(TE &res) {
static char ch;
while((ch=getchar())<'0'||ch>'9');
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-'0';
}
const int N = 20000 + 100;
const int M = 100000 * 2 + 1000;
struct Edge {
int v, w, next;
}t[M];
int head[N], num = 0;
inline void insert( int u, int v, int w ) {
t[++num]. v = v;
t[num]. w = w;
t[num]. next = head[u];
head[u] = num;
}
int volk[N];
bool dfs( int u, int f, int limit ) {
for( int i = head[u]; i; i = t[i].next ) {
int v = t[i].v;
if( v == f ) continue;
if( t[i].w > limit ) {
if( volk[v] == volk[u] ) return false;
if( !volk[v] )
if( !dfs( v, u, limit ) )return false;
}
}
return true;
}
int n, m, maxw;
int main() {
readin( n );
readin( m );
maxw = 0;
for( int i = 1; i <= m; i++ ) {
int u, v, w;
readin( u );
readin( v );
readin( w );
maxw = max(maxw,w);
insert( u, v, w );
insert( v, u, w );
}
int L = 0, R = maxw;
while( L != R ) {
memset( volk, 0, sizeof(volk) );
int mid = ( L + R ) >> 1, ok = 1;
for( int i = 1; i <= n; i++ )
if( !volk[i] ) {
volk[i] = 1;
ok = dfs( i, i, mid );
if( !ok ) break;
}
if( ok ) R = mid;
else L = mid + 1;
}
printf( "%d\n", L );
return 0;
}
法2:并查集,用n+i表示i的反人。先排序,再从大到小。
O(nlogn)
这是一种很神奇的思想,先从大到小排序,处理每一对关系。
若fa[i]=fa[j],则表示i与j同狱。
若fa[i]=fa[n+j](fa[j]=fa[n+i]),则表示i与j不同狱。
只要出现同狱,就输出当前关系仇恨值,可以保证是最小的(贪心思想)。
提供者:lemonoil
#include <cstdio>
#include <algorithm>
using namespace std;
struct N {
int x, y, w;
}a[100000+100];
int n, m, fa[20000*2+100];
bool comp( const struct N &a, const struct N &b ) {
return a.w > b.w;
}
int find( int x ) {
if( fa[x] == x ) return x;
return fa[x] = find(fa[x]);
}
int main() {
freopen( "prison.in", "r", stdin );
freopen( "prison,out", "w", stdout );
scanf( "%d%d", &n, &m );
for( int i = 1; i <= m; i++ )
scanf( "%d%d%d", &a[i].x, &a[i].y, &a[i].w );
for( int i = 1; i <= 2*n; i++ )
fa[i] = i;
sort( a+1, a+m+1, comp );
for( int i = 1; i <= m; i++ ) {
int fax = find(a[i].x), fay = find(a[i].y);
if( fax == fay ) {
printf( "%d\n", a[i].w );
return 0;
}
fa[fax] = find( a[i].y + n );
fa[fay] = find( a[i].x + n );
}
printf( "0\n" );
return 0;
}
T4 flow
洛谷P1514 NOIP2010 引水入城
这一道题,不算太难。但当然,考场上我没有做出来。刚看见这道题时简直蒙了,但细细思考还是离正确答案更近了一步。
不过,自己做的题还是不够,很难在短时间内自己把模型给建立出来。幸好,伟大的文学家、哲学家、社会运动家Scanf Hu曾多次耳濡目染,多写暴力是人类进步的阶梯。
又不过,对于这道题,我连暴力都写不出来。于是,仔细分析数据过后,我采用特判,解决了3个不能满足要求的数据点,得到了30分。
而下面贴的是正解。我们从题目的该图片中,可以得到一点启示。
第一排有m个位置可建蓄水厂,我们可以证明,如果能满足要求,每一个建成的蓄水厂都可以对应一段连续区间的城市(使用反证法)。这一切,只需要一个DFS以
O(n)
的复杂度来解决。
而我们所要做的,便是从这m个中挑选出最少的点,使得它们能够覆盖整个区间,这就是区间覆盖问题。对此我们仅需套一个简单DP,下面给出了两种解法。
由于该题目的性质,最终挑选出的结点一定能包含自己正下方的点,可以凭此进行一点小优化。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int OO = 0x3f3f3f3f;
template<class TE> inline void readin(TE &res) {
static char ch;
while((ch=getchar())<'0'||ch>'9');
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-'0';
}
const int N = 500 + 10;
const int M = 500 + 10;
int n, m, height[N][M], l[N][M], r[N][M], dp[M];
bool vis[N][M];
void dfs( int i, int j );
void search( int i, int j, int x, int y ) {
if( x >= 1 && x <= n && y >= 1 && y <= m && height[x][y] < height[i][j] ) {
if( !vis[x][y] ) dfs( x, y );
l[i][j] = min(l[i][j],l[x][y]);
r[i][j] = max(r[i][j],r[x][y]);
}
}
void dfs( int i, int j ) {
vis[i][j] = 1;
int x = i - 1, y = j;
search( i, j, x, y );
x = i + 1, y = j;
search( i, j, x, y );
x = i, y = j - 1;
search( i, j, x, y );
x = i, y = j + 1;
search( i, j, x, y );
}
int main() {
freopen( "flow.in", "r", stdin );
freopen( "flow.out", "w", stdout );
readin(n);
readin(m);
for( int i = 1; i <= n; i++ )
for( int j = 1; j <= m; j++ )
readin( height[i][j] );
memset( l, OO, sizeof(l) );
memset( r, 0, sizeof(r) );
memset( dp, OO, sizeof(dp) );
int ans = 0;
for( int j = 1; j <= m; j++ )
l[n][j] = r[n][j] = j;
for( int j = 1; j <= m; j++ )
if( !vis[1][j] )
dfs( 1, j );
for( int j = 1; j <= m; j++ )
if( !vis[n][j] )
ans++;
if( ans != 0 ) {
printf( "0\n%d\n", ans );
return 0;
}
dp[0] = 0;
for( int j = 1; j <= m; j++ ) {
for( int k = j; k <= r[1][j]; k++ )
for( int p = l[1][j] - 1; p <= j; p++ )
dp[k] = min(dp[k],dp[p]+1);
}
/*
for( int k = 1; k <= m; k++ )
for( int j = 1; j <= k; j++ )
if( k <= r[1][j] )
for( int p = l[1][j] - 1; p <= j; p++ )
dp[k] = min(dp[k],dp[p]+1);
*/
printf( "1\n%d\n", dp[m] );
return 0;
}
经验与教训
这是第一次在机房里考历年真题,经验还是很不足。但是,只有凭此去一步步地完善自我,提升自我,才能真正得到质的飞跃。
修改前,只有130,很低很低。加上入学考试的不如意,寒假毕竟学习了20天的竞赛。这一切,差一点就让我崩溃了。
然而,事后再想想,一切其实并没有那么糟。竞赛不过是第7门主科,总会有起起落落。只要相信自己,掌握方法,学会付出,善于总结与创新实践,就一定能让未来的自己感到欣慰。加油!