NOIP 2009 模拟测试总结

NOIP 2009 模拟测试总结NOIP2009模拟测试总结前序题目的难易安排挺合适的,第一题Spy和第二题Son都可以简单通过,从第三题开始就有点难度了。第三题Trade就有点难度,代码倒不难,只是模型转换和双向BFS方法难想到(其实第四题DFS也是万万想不到的),还有Tarjan加上拓扑排序的方法有些恶心……第四题靶形数独,貌似只能搜索,只不过有技巧。好了,开始正题。Question1:潜伏者Spy非常简单的一道题,

大家好,欢迎来到IT知识分享网。NOIP

NOIP 2009 模拟测试总结

前序

题目的难易安排挺合适的,第一题Spy和第二题Son都可以简单通过,从第三题开始就有点难度了。第三题Trade就有点难度,代码倒不难,只是模型转换和双向BFS方法难想到(其实第四题DFS也是万万想不到的),还有Tarjan加上拓扑排序的方法有些恶心……第四题靶形数独,貌似只能搜索,只不过有技巧

好了,开始正题。

Question1: 潜伏者 Spy

非常简单的一道题,可以用字母的ASCLL码或直接用map建立映射。
直接上代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;

int tot = 0;
char ss1[105], ss2[105], ori[105];
bool word[27], exi[27];
map<char, char> trans;

int main()
{
    freopen( "spy.in", "r", stdin );
    freopen( "spy.out", "w", stdout );
    memset( word, false, sizeof(word));
    memset( exi, false, sizeof(exi));
    scanf( "%s", ss1 );
    scanf( "%s", ss2 );
    scanf( "%s", ori );
    int len = strlen(ss1);
    for( int i = 0; i < len; i++ )
    {
        if( !exi[ss2[i]-'A'] ) tot++;
        exi[ss2[i]-'A'] = true;
        if( !word[ss1[i]-'A'] || trans[ss1[i]] == ss2[i] )
        {
            trans[ss1[i]] = ss2[i];
            word[ss1[i]-'A'] = true;
        }           
        else
        {
            printf( "Failed\n" );
            return 0;
        }
    }
    if( tot < 26 ) 
    {
        printf( "Failed\n" );
        return 0;
    }
    int len1 = strlen(ori);
    for( int i = 0; i < len1; i++ )
    {
        if( !word[ori[i]-'A'] )
        {
            printf( "Failed\n" );
            return 0;
        }
    }
    for( int i = 0; i < len1; i++ )
    {
        printf( "%c", trans[ori[i]] );
    }
    return 0;
}

Question2: Hankson的趣味题 Son

小小的数论题,也是两种做法,一是枚举加判断,而是质因数分解。第一种好写很多,如果是直接写的话没法得满,要有点小技巧。

也不多说直接上代码:

组合判断套装

int gcd( int a, int b )
{
    b == 0 ? a : gcd( b, a%b );
}
int lcm( int a, int b )
{
    return a / gcd( a, b ) * b;
}
int cal( int x )
{
    if( gcd( x, a0 ) == a1 )
        if( lcm( x, b0 ) == b1 ) ans++;
}

循环枚举

while( n-- )
    {
        ans = 0;
        scanf( "%d%d%d%d", &a0, &a1, &b0, &b1 );
        for( int i = 1; i * i <= b1; i++ )
        {
            if( b1%i == 0 )
            {
                cal( i );
                if( i * i != b1 ) cal( b1 / i );
            }
        }
        printf( "%d\n", ans );
    }

有一点要注意,只用枚举到根号b1就行,否则要超时。


Question3: 最优贸易 Trade

本来一开始写的Tarjan,但是在缩点上出了点问题,最后折中输出了贪心最优值。考下来评讲才发现原来能用双向SPFA,非常巧妙!

具体思路就是,要得到最大利润,就要较低买入,较高卖出,所以就可以建立两个图,一个正向图(只保存正向边),一个反向图(只保存反向边),为什么呢?

其实是这样的。低价买入在高价卖出之前,所以我们要先找到一个从起点出发能够达到的最低价格(用low[i]数组保存从起点到i城能最低买入的价格),再用反向图从终点出发,寻找每一个能够到达的城市的最高卖出价(用high[i]表示从终点出发到i城能够卖出的最高价) ,到了这时,每个城市都有了两个属性——从起点到这个城市一路上最低买入价从终点到这个城市一路上最高卖出价,又终点和起点肯定是相通的,就只需枚举每一个城市计算高价减去低价的值,再在其中寻找最大值就行了。

代码:

inline void spfa_1()
{
    q.push(1);
    vis[1] = true;
    MIN[1] = Data[1];
    while(!q.empty()){
        int x = q.front();
        q.pop();
        vis[x] = false;
        for( int i = head[x][0];i;i = edge[i].next )
        {
            int v = edge[i].v;
            if( MIN[v] > MIN[x] || MIN[v] > Data[v] )
            {
               MIN[v] = min( MIN[x],Data[v] );
               if( !vis[v] ) q.push(v),vis[v] = true;
            }
        }
    }

}

inline void spfa_2()
{
    q.push(n);
    vis[n] = true;
    MAX[n] = Data[n];
    ans = max( ans,MAX[n] - MIN[n] );
    while( !q.empty() )
    {
        int x = q.front();
        q.pop();
        vis[x] = false;
        for( int i = head[x][1];i;i = edge[i].next )
        {
            int v = edge[i].v;
            if( MAX[v] < MAX[x] || MAX[v] < Data[v] )
            {
               MAX[v] = max( MAX[x],Data[v] );
               ans = max( MAX[v] - MIN[v],ans );
               if( !vis[v] ) q.push(v),vis[v] = true;
            }
        }
    } 
}

MAX[] 和MIN[] 分别保存上面提到的最高价和最低价。


Question4: 靶形数独 Sudoku

这题正解用深搜是我没想到的(不过也只有9 * 9 ,也是有道理的)
也算见识到了深搜的强大。
主要思想就是类似八皇后的回溯,搜索->放置->进入下一层->到达边界返回->复原再搜…..
思路非常简单,但是有技巧。首先只深搜是过不了的,很明显会TLE,所以要明智的选取对象。什么意思呢?
我们来回忆我们怎么做数独的。我们会选取剩余可能性最小的格子去填,这也就是核心思想,这可以使得搜索树变小,也就优化了时间,再加上二进制位运算来优化数字选取的过程,就可以过了。鉴于网上二进制的代码很多,我就放一个不用二进制的,初学者可以看一下。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int map[10][10], val[10][10] = { {
  
  0, 0, 0, 0, 0, 0, 0, 0, 0},
                                 {
  
  0,6 ,6 ,6 ,6 ,6 ,6 ,6 ,6 ,6},
                                 {
  
  0,6 ,7 ,7 ,7 ,7 ,7 ,7 ,7 ,6},                              
                                 {
  
  0,6, 7, 8, 8, 8, 8, 8, 7, 6},
                                 {
  
  0,6 ,7 ,8 ,9 ,9 ,9 ,8 ,7 ,6},
                                 {
  
  0,6 ,7 ,8 ,9 ,10, 9, 8, 7, 6},
                                 {
  
  0,6 ,7 ,8 ,9 ,9 ,9 ,8 ,7 ,6},
                                 {
  
  0,6, 7, 8, 8, 8, 8, 8, 7, 6},
                                 {
  
  0,6 ,7 ,7 ,7 ,7 ,7 ,7 ,7 ,6}, 
                                 {
  
  0,6 ,6 ,6 ,6 ,6 ,6 ,6 ,6 ,6},
                                };
int box[10][10], ans = -1;
bool vis1[10][10], vis2[10][10], vis3[10][10];

inline void dfs()
{
    int step = 10, x, y;
    for( register int i = 1; i <= 9; i++ )
    for( register int j = 1; j <= 9; j++ )
    {
        if( !map[i][j] )
        {

            int cnt = 0;
            for( int k = 1; k <= 9; k++ )
                if( !vis3[box[i][j]][k] && !vis1[i][k] && !vis2[j][k] )
                    cnt++;
            if( cnt < step )
            {
                step = cnt;
                x = i, y = j;
            }
        }               
    }               
    if( step == 10 )
    {
        int tot = 0;
        for( int i = 1; i <= 9; i++ )
            for( int j = 1; j <= 9; j++ )
                tot += map[i][j] * val[i][j];
        ans = max( ans, tot );
        return; 
    }
    for( int k = 1; k <= 9; k++ )
    {
        if( !vis3[box[x][y]][k] && !vis1[x][k] && !vis2[y][k] )
        {
            vis1[x][k] = vis2[y][k] = vis3[box[x][y]][k] = true;
            map[x][y] = k;
            dfs();
            map[x][y] = 0;
            vis1[x][k] = vis2[y][k] = vis3[box[x][y]][k] = false;
        }
    }   
}
int main()
{
    memset( vis1, false, sizeof(vis1));
    memset( vis2, false, sizeof(vis2));
    memset( vis3, false, sizeof(vis3));
    for( int i = 1; i <= 9; i++ )
        for( int j = 1; j <= 9; j++ )
        {
            cin>>map[i][j];
            box[i][j] = (i-1) / 3 * 3 + (j-1) / 3 + 1;
            if( map[i][j] )
            {       
                /**/if( vis1[i][map[i][j]] || vis2[j][map[i][j]] || vis3[box[i][j]][map[i][j]] )
                {
                    printf( "-1\n" );
                    return 0;
                }
                vis1[i][map[i][j]] = true;
                vis2[j][map[i][j]] = true;  
                vis3[box[i][j]][map[i][j]] = true;
            }
        }
    dfs();
    printf( "%d\n", ans );
    return 0;       
}

九宫格的判断还可以像我直接打表一样自己写在程序里。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/22558.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信