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;
}

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

   

原文地址:https://www.cnblogs.com/yefeng1627/p/2829342.html