zoukankan      html  css  js  c++  java
  • hut_csust 新生友谊赛题目解析

    先给出 Lyush 大神的部分解题思路

    HDU-4255 A Famous Grid BFS 
    知识点:素数筛选+模拟 
    难度:中等
    Source Fudan Local Programming Contest 2012
    思路:得到两个表,一个素数表,一个是矩阵表,然后搜索即可,一个要注意的地方是要把矩阵打的稍微大一点,因为可能要走到外面去。
    
    HDU-4386 Quadrilateral
    知识点:海伦公式推广,数学
    难度:中等
    Source 2012 Multi-University Training Contest 9
    思路:这题就是一个结论了,当四边形为圆的内接四边形,面积最大。设四边长为abcd,半周长为p,则最大面积=sqrt((p-a)*(p-b)*(p-c)*(p-d))
    
    POJ-2785 4 Values whose Sum is 0
    知识点:hash 或者 二分
    难度:中等
    Source Southwestern Europe 2005
    思路:两种做法,第一就是枚举出两个n*n的集合,然后再利用hash操作来得出结果。还有一种解法就是用到了二分查找。
    
    POJ-2027 No Brainer
    签名题
    
    ZOJ-3622 Magic Number
    知识点:想法,找规律
    难度:中等
    思路:打表,找规律(直接肯定不行)。一共也只有不超过50个数,可以直接用数组存下来。

    题目来源

    IDOriginTitle
    Problem A HDU 4255 A Famous Grid
    Problem B HDU 4386 Quadrilateral
    Problem C POJ 2785 4 Values whose Sum is 0
    Problem D POJ 2027 No Brainer
    Problem E ZOJ 3622 Magic Number
    Problem F HDU 4300 Clairewd’s message
    Problem G HDU 4310 Hero
    Problem H HDU 4311 Meeting point-1
    Problem I POJ 1061 青蛙的约会

    A题:

      预处理生成200*200的图形后,再筛选出10000以内素数进行标记,之后使用BFS(广度优先搜索即可) 

    参考代码:

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<iostream>
    #include<string.h>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    
    const int N = 200;
    const int maxn = 100000;
    int mp[N+10][N+10];
    int p[10000], size;
    bool vis[maxn+10];
    
    void InitGraph(int key, int x, int y, int n){    
    //    printf("x = %d, y = %d\n", x, y);
        if( n <= 1 ) return;
        int r , c ;
        for(r = x, c = y; c <= y+n-1; c++)
            mp[r][c] = key--;
        for(r = x+1, c = y+n-1; r <= x+n-1; r++)
            mp[r][c] = key--;
        for(r = x+n-1, c = y+n-2; c >= y; c--)
            mp[r][c] = key--;
        for(r = x+n-2, c = y; r > x; r--)
            mp[r][c] = key--;
        r = x+1; c = y+1; n -= 2;    
        InitGraph( key, r, c, n );  
    }
    void GetPrime(){
        memset( vis, 0, sizeof(vis));
        vis[0] = vis[1] = true;
        for(int i = 2; i < maxn; i++)
        {
            if( !vis[i] ) p[size++] = i;
            for(int j = 0; j < size && p[j]*i < maxn; j++)
            {
                vis[ p[j]*i ] = true;
                if( i%p[j] == 0 ) break;
            }
        }
    //    printf("%d\n", size );
    }
    void test(){
        for(int i = 1; i <= N; i++)
        {
            for(int j = 1; j <= N; j++)
                printf("%5d", mp[i][j] );
            puts("");
        }
    }
    
    struct node{
        int x, y, step;
    }st,ed,info,nxt;
    bool used[N+10][N+10];
    int dir[4][2] = { {0,-1},{-1,0},{1,0},{0,1} };
    queue<node> Q;
    
    bool legal( int x, int y)
    {
        if( x >= 1 && x <= N && y >= 1 && y <= N )
                return true;
        return false;
    }
    void BFS(int s,int t){
        for(int i = 1; i <= N; i++)
            for(int j = 1; j <= N; j++)
            {
                if( mp[i][j] == s ){st.x = i;st.y = j;st.step = 0; }
                if( mp[i][j] == t ){ed.x = i;ed.y = j; } 
            }
        while( !Q.empty() ) Q.pop();
        memset( used, 0, sizeof(used));
        used[ st.x ][ st.y ] = true;
        Q.push(st);
        while( !Q.empty() ){
            info = Q.front(); Q.pop();
        //    printf("x = %d, y = %d\n", info.x, info.y );    
            if( info.x == ed.x && info.y == ed.y ){
                printf("%d\n", info.step);
                return;
            }
            for(int i = 0; i < 4; i++)
            {
                int x = info.x+dir[i][0], y = info.y+dir[i][1];
                if( legal(x,y) && vis[ mp[x][y] ] && !used[x][y])
                {
                    used[x][y] = true;
                    nxt.x = x; nxt.y = y; nxt.step = info.step+1;
                    Q.push(nxt);    
                }
            }
        }
        puts("impossible");
    }
    int main(){
    
        InitGraph( N*N, 1, 1, N );// test();
        GetPrime();    
        int s, t, T = 1;
        while( scanf("%d%d",&s,&t) != EOF)
        {
            printf("Case %d: ", T++);    
            BFS(s,t);
        }    
        return 0;
    }

    B题:

      结论题,海伦公式推广到四边形,要注意限定在圆内接四边形 

    证明:
    记圆内接四边形ABCD的四条边依次为a、b、c、d,则连接一条对角线AC,其对角为∠B和∠D并且∠B + ∠D = 180°   -----------我给的角度不一定对应,你自己画个图吧
    根据余弦定理:
    AC² = a² + b² - 2abcos∠B 
    AC² = c² + d² - 2cdcos∠D   = c² + d² +  2cdcos∠B        -------已经用到 ∠B + ∠D = 180°
    所以: a² + b² - 2abcos∠B =  c² + d² +  2cdcos∠B
    得到:cos∠B = (a² + b² - c² - d² ) / (2ab + 2cd)
    sinB = sinD = √(1 - cos²B) = √[(2ab + 2cd)²- (a² + b² - c² - d² )²]       /  (2ab + 2cd)
           .........用平方差公式 略....= √[ (a+b+c-d)(a+b-c+d) (a-b+c+d)(-a+b+c+d)] /  (2ab + 2cd)
    S圆内接四边形面积 =  S△ABC + S△ABD =  absinB / 2 + cdsinD / 2
                                    = (ab + cd)absinB / 2 
                                    = √[(s - a)(s - b)(s - c)(s - d)]

    参考代码:  

    View Code
     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<algorithm>
     4 #include<math.h>
     5 using namespace std;
     6 const double esp = 1e-8;
     7 double getarea( int a, int b, int c, int d)
     8 {
     9     double p = (a+b+c+d)/2.;
    10     return sqrt( (p-a)*(p-b)*(p-c)*(p-d) );    
    11 }
    12 int main(){
    13     int T, a[4];    
    14     scanf("%d", &T);
    15     for(int Case = 1; Case <= T; Case++ )
    16     {
    17         for(int i = 0; i < 4; i++)
    18             scanf("%d", &a[i] );
    19         printf("Case %d: ", Case);        
    20         sort( a, a+4 );    
    21         if( a[0]+a[1]+a[2] <= a[3] ) printf("-1\n");
    22         else
    23         {
    24             double ans = getarea( a[0], a[1], a[2], a[3] );
    25             printf("%.6lf\n", ans);    
    26         }
    27     }
    28 }

    C题:

      因为N = 4000, 则我们可以得出A,B集合的所有组合情况后存储起来,  然后再计算C,D集合组合情况的时候查询 -( C[i] + D[j] ) 是否属于 A,B组合集合中.

      在存储 A,B集合组合情况, 我们需要接近O(1)时间进行查询. 

      这里,我们使用哈希的思想: 对于一个数 X, 我们将它对一个大素数P取模,

      可能有部分数对P取模后,结果相同, 则我们对于任意一个 X%P ,建立一个链表, 链表节点上存储X本身. 

      这样,我们可以在接近O(1)的时间查询值X,是否存在与这一条链上.

    参考代码:

    View Code
    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    using namespace std;
    
    const int Mod = 10000007;
    const int N = 5000;
    typedef long long LL;
    struct node{
        
        int key,cnt,next;
    }edge[Mod+10];
    int head[Mod+10], idx; 
    
    int a[N], b[N], c[N], d[N];
    int n;
    void insert( int x ){
        int u = x%Mod; if(u < 0) u += Mod;
        bool flag = true;    
        for(int i = head[u]; ~i ; i = edge[i].next) // 查询是否已存在
            if( edge[i].key == x ){ edge[i].cnt++; return; }     
         // 不存在时插入
        edge[idx].key = x; edge[idx].next = head[u];
        edge[idx].cnt = 1; head[u] = idx++;
        
    }
    int find( int x ){
        int u = x%Mod; if(u < 0) u += Mod;
        for(int i = head[u]; ~i; i = edge[i].next)
            if( edge[i].key == x ) return edge[i].cnt;
        return 0;
    }
    int main(){
        while( scanf("%d", &n) != EOF)
        {
                memset( head, 0xff, sizeof(head)); idx = 0;    
                for(int i = 0; i < n; i++)
                    scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
                for(int i = 0; i < n; i++)
                    for(int j = 0; j < n; j++)
                        insert( a[i]+b[j] );    
                        LL ans = 0;
                for(int i = 0; i < n; i++)
                    for(int j = 0; j < n; j++)
                        ans += find( -(c[i]+d[j]) ); 
        
                printf("%lld\n", ans );    
        }
        return 0;
    }

    D题:

      签到题. 

    参考代码:

    View Code
    #include<stdio.h>
    int main(){
        int n, x, y;
        scanf("%d",&n);    
        while(n--)    
        {
            scanf("%d%d", &x,&y);
            puts( x >= y ? "MMM BRAINS" : "NO BRAINS" );
        }
        return 0;
    }

    E题:

      z = [ (x*10^y)+y ] % y  == 0

      若 Y 要满足此关系, 则 必定存在一个正整数 K, 使 K*Y 为 10^n, 例如 Y = 5, K = 2,     Y = 25 ,K  = 4

      简单分析可以知道满足此要求的Y 有  1, 5, 25, 125 以及它们的 10^k 倍数

      2^31-1 范围内, 总数不到100 ,则我们可以预处理出来后存储于辅助数组Q中,

      则结果为  (1,m) - (1,n-1)   (其中(1,m)为区间(1,m)中Q数组中出现的元素个数 )

    参考代码:

    View Code
    #include<stdio.h>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    typedef long long LL;
    
    const LL inf = 0xffffffff;
    LL Q[101];
    int n;
    void init(){
        n = 0;    
        int a[5] = {1,2,5,25,125};
        for(int k = 0; k < 5; k++)
        {
            for(int i = 0;;i++)    
            {
                LL tmp = a[k]*(LL)(pow(10,i));
                if( tmp > inf ) break;
                else
                    Q[ n++ ] = tmp;
            }    
        }
        sort( Q, Q+n );
    //    for(int i = 0; i < n; i++)
    //        printf("%lld\n", Q[i]);
    }
    
    int sum( LL x )
    {
        int res = 0;    
        for(int i = 0; i < n; i++)
            if( x >= Q[i] ) res++;
            else    return res;
        return res;
    }
    int main()
    {
        init();
        LL a, b;
        while( scanf("%lld%lld",&a,&b) != EOF)
        {
            printf("%d\n", sum(b)-sum(a-1) );    
        }
        return 0;
    }

    F题: 

    题目大意:

      密文翻译,密文由小写字母构成,现给出a-z的对应密文字符。
      现有一段密文和明文,但是被打断了,仅知道密文在前,明文在后,
      让你输出最短的密文+明文序列。

      解法二: 此题也可使用扩展KMP
    解题思路:
      取原串后面半截,同原串译文匹配,记录下能匹配到的最大长度
    即为答案。这里有点小问题,待笔者斟酌后再行修改, 先给出目前解题代码

    View Code
    #include<iostream>
    #include<string>
    #include<map>
    using namespace std;
    const int N = 101010;
    map<char,char>mm;
    void solve(string s1,string s2){
        int mid = ((int)s1.length()+1)/2; 
        string s = s1.substr( mid, (int)s1.length()-mid+1 );
        int pos = 0,cnt = 0;
        for(int i = 0; i < (int)s.length(); i++){
            if(s[i] == s2[pos]) pos++;
            else                pos = 0;    
        }    
        int len = mid+ ((int)s.length()-pos);
        //cout << "s1.length = " << s1.length() << endl;
        //cout << "s2.length = " << s2.length() << endl;
        string ans1 = s1.substr(0,len);
        string ans2 = s2.substr(0,len);
        cout << ans1+ans2 << endl;
    }
    int main(){
        int t; cin>>t;
        while(t--){
            string tab, s1, s2;
            cin>>tab>>s1; mm.clear();
            for(int i = 0; i < 26; i++ ){
                mm[ tab[i] ] = i+'a';    
            }
            for(int i = 0; i < (int)s1.length(); i++){
                s2 += mm[ s1[i] ];    
            } 
        //    s2[(int)s1.length()] = '\0';
            solve(s1,s2);
        }
        return 0;        
    }

    G题:

      解法一: 贪心,按 DPS/HP的比例击杀对方英雄

      解法二: DP

    贪心解法代码
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    #include<string.h>
    #include<iostream>
    using namespace std;
    
    struct node{
        int hp, dps;
        double k;
        void read(){
            scanf("%d%d",&dps,&hp);
            k = 1.0*dps/hp;
        }
        bool operator < (node tmp)const 
        {
            return k > tmp.k;    
        }
    }h[21];
    
    int main(){
        int n;
        while( scanf("%d", &n) != EOF)
        {
            for(int i = 0; i < n; i++)
                h[i].read();
            sort( h, h+n );
            int sum[21];    
            memset( sum, 0, sizeof(sum));    
            for(int i = 0; i < n; i++)
            {
                if( i == 0 ) sum[i] = h[i].dps;
                else    sum[i] = sum[i-1]+h[i].dps;
    //            printf("sum[%d] = %d\n", i, sum[i] );    
            }
            int res = sum[n-1]*h[0].hp;    
            for(int i = 1; i < n; i++)
            {
                res += (sum[n-1]-sum[i-1])*h[i].hp;    
            }
            printf("%d\n", res);    
        }
        return 0;
    }
    DP状态压缩
    #include<iostream>
    #include<algorithm> 
    using namespace std;
    const int inf = 0x7fffffff; 
    const int N = (1<<21);
    
    int dp[N],hp_sum[N]; 
    int hp[25], dps[25], n; 
    int main(){
        while(scanf("%d",&n)==1){
            for(int i = 0; i < n; i++)
                scanf("%d%d",dps+i,hp+i);
            memset(hp_sum,0,sizeof(hp_sum));
            for(int mask = 0; mask < (1<<n); mask++){
                int i = 0, k = mask;   dp[mask] = inf; 
                while(k){
                    if(k&1) hp_sum[mask] += hp[i];
                    k >>= 1; i++; 
                }
                //printf("hp_sum[%d] = %d\n", mask, hp_sum[mask]);    
            }     
            dp[0] = 0; 
            for(int mask = 1; mask < (1<<n); mask++){
                for(int i = 0; (1<<i) <= mask; i++){
                    if( (mask>>i)&1 == 1 ){
                        dp[mask] = min(dp[mask], dp[mask-(1<<i)]+hp_sum[mask]*dps[i]); 
                    } 
                }    
            } 
            printf("%d\n", dp[(1<<n)-1]); 
        //    for(int mask = 0; mask < (1<<n); mask++) 
        //        printf("%d\n", dp[mask] ); 
        } 
        return 0;    
    } 

    H题:

      对于任意一点 (x0,y0), 其他点( xi,yi )到其距离和为

      | x0 - xi | + | y0 - yi |     (其中i 取值范围为 [1,n] 中去除x0,y0点) 

      我们可以分别求 x, y,  

      将x从小到大排序后, 对于小于 x0的, 有  ( i - 1 ) * x0 - sum( 0,i-1 )   ( sum( i,j )表示 i, j区间求和 )

      对于大于x0的,有 sum( i, n ) - ( n - i ) * x0       

      同样的方法处理Y, 处理前缀和时间复杂度为O(n), 排序时间复杂度为 O(nlog(N))

      总体时间复杂度为 O( Nlog(N) )

    参考代码:

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    #define MIN(a,b) (a)<(b)?(a):(b)
    typedef __int64 LL;
    
    const int N = 100007;
    
    struct node{
        int x, y, id;
    }p[N];
    
    int n;
    LL A[N], B[N], sum[N];
    
    bool cmpx( node a, node b){
        return a.x < b.x;
    }
    bool cmpy( node a, node b){
        return a.y < b.y;
    }
    int main(){
        int T;
        scanf("%d", &T);
        while( T-- ){
            scanf("%d",&n);
            for(int i = 1; i <= n; i++)
            {
                scanf("%d%d", &(p[i].x), &(p[i].y) );
                p[i].id = i;
            }
            // solve x
            sort( p+1, p+n+1, cmpx );
            sum[0] = 0;
            for(int i = 1; i <= n; i++)
                sum[i] = sum[i-1]+p[i].x;
            for(int i = 1; i <= n; i++)
            {
                A[ p[i].id ] = 1LL*p[i].x*(i-1) - sum[i-1];
                A[ p[i].id ] += (sum[n] - sum[i]) - 1LL*p[i].x*(n-i);
            }    
            // solve y    
            sort( p+1, p+n+1, cmpy );
            sum[0] = 0;
            for(int i = 1; i <= n; i++)
                sum[i] = sum[i-1]+p[i].y;
            for(int i = 1; i <= n; i++)
            {
                B[ p[i].id ] = 1LL*p[i].y*(i-1) - sum[i-1];
                B[ p[i].id ] += (sum[n] - sum[i]) - 1LL*p[i].y*(n-i);
            }
            LL ans = A[1]+B[1]; 
            for(int i = 2; i <= n; i++)
            {
                ans = MIN( ans, A[ p[i].id ]+B[ p[i].id ] );    
            }
            printf("%I64d\n", ans );    
        }
        return 0;
    }

    I 题:

      转换成线性同余方程后,使用扩展GCD解即可.

      设 k1 次后两青蛙相遇, 则满足条件:

        (  k1 * m + x ) % L = ( k1 * n + y ) % L

    =>  (  k1 * m + x ) % L -  ( k1 * n + y ) % L = 0

    =>  ( (  k1 * m + x )  -  ( k1 * n + y )  ) % L = 0

    =>  ( k1 * ( m - n ) + ( x - y ) )  % L = 0

    =>  k1 * ( m - n ) + ( x - y ) = L * k2     ( 其中 k2 为存在正整数 K2)

    =>  令 a  = m - n , b =  -L, c = y-x

    得  a * k1 + b * k2 = c  得线性同余方程, 使用 扩展GCD计算 得出解 即可      扩展GCD证明过程见笔者Q空间.   Q: 361072774

    View Code
    #include<stdio.h>
    #include<math.h>
    typedef long long LL;
    
    LL exgcd( LL a, LL b, LL &x, LL &y)
    {
        if( b == 0 ){
            x = 1; y = 0;
            return b;
        }
        int r = exgcd( b, a%b, x, y );
        int t = x;
        x = y;
        y = t - (a/b)*y;
        return r;
    }
    LL gcd( LL a, LL b )
    {
        return b == 0 ? a : gcd( b, a%b );
    }
    int main(){
        LL x, y, m, n, L;
        while( scanf("%lld%lld%lld%lld%lld", &x,&y,&m,&n,&L) != EOF)
        {
            LL a = m-n, b = -L, c = y-x;
            LL x, y, d = gcd( a, b );
            if( c%d != 0 ) puts("Impossible");
            else{
                a /= d; b /= d; c /= d;
                exgcd( a, b, x, y );
                x = c*x%b;
                if( x < 0 ) x += b;
                printf("%lld\n", x );
            }
        }
        return 0;
    }

      以上内容仅为笔者解题思路. 由于时间仓促,加之能力有限,欢迎读者指出不当处!

       

  • 相关阅读:
    iOS 字符串的UTF8 编码 以及归档反归档
    iOS 关于关键字高亮
    iOS 适配 ,关于prefix Header 文件的配置
    关于collectionView 的头视图
    关于页面的下拉刷新,和上拉加载 --- > collectionView ,tableView
    Nums数独计算器的感谢版,有3种解题方式,过去注册的也是可以用
    Nums数独计算器,良心软件,给不给钱都是可以的
    人生不过是给自己找一些痕迹,证明自己存在过
    做些有益的事情,人生要懂得自重
    振作才是自己可以走的路,人生不要放弃自己的争取
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2829342.html
Copyright © 2011-2022 走看看