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个数,可以直接用数组存下来。

 

 

 

 

题目来源

ID Origin Title
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;

}

 

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

   

 

你可能感兴趣的:(解析)