码迷,mamicode.com
首页 > 其他好文 > 详细

HLJU周赛5解题报告

时间:2015-06-24 22:40:11      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:

Problem A: 求第K大数

Time Limit: 2 Sec  Memory Limit: 128 MB
Submit: 81  Solved: 32
[Submit][Status][Web Board] [Edit] [TestData]

Description

一天,喜欢看电影的DoubleQ 和 XXZ 来到一个神奇的电影院,这个电影院可以同时放映n部电影,每部电影都有相应的票价xi,面对着杀破狼2,侏罗纪世界,少年班等等好看的电影,他们最后商量,看票价第K大的电影,但是由于电影太多,两个人不知道第K大的票价是多少,现在两个人想知道第K大的票价是多少,聪明的你能帮助他们从茫茫影海找出票价第K大是多少吗?

Input

有多组数据

第1行 两个整数N, K以空格隔开;
第2行 有N个整数(可出现相同数字,均为随机生成),同样以空格隔开。
0 < n ≤ 5*10^6 , 0 < k ≤ n
1 ≤ xi ≤ 10^8

Output

输出第 k 大的数字。

Sample Input

5 2
5 4 1 3 1

Sample Output

4

这道题我是精心设计的数据,主要是因为最近看了很多面试经验贴,发现这道题目出现的频率非常高,面试官让面试者手写算法不准用STL里面的nth_element()函数,然后很多人不会。正好这次我把这题放到周赛里面,卡STL是比较难的,数据不好把握,因为STL里面的nth_element已经封装的非常好了,我在没优化之前标程和STL跑的时间之差几十ms,为了卡STL我加了一个读入优化,将程序控制在720~920ms之间,STL跑的是1500ms,因此我将时间设置1s,这样才能很好的发挥题目效果。。。。可能我数据卡的实在是太紧,没人过。。。。然后将1s改成2s了然后一大波人STL水过去了。。。。


说下正确解法:

利用快速排序的思想,但是不必全部排序,只要当i位置已经是第K大就直接退出,这时候i的前面数都比i位置的数大,i后面的数都比i小,但都是无序的,平均时间复杂度是O(n),最坏时间复杂度是O(n^2),当然如果想进一步优化,算法导论上面有最坏是O(n)的解法,有兴趣可以看一下


读入优化是优化scanf输入,改成gechar()输入,会快很多。

#include <bits/stdc++.h>  
#define N 6000010  
int a[N];  
int n,k;  
  
int input()  
{  
    int ans=0;  
    char a;  
    while((a=getchar())<'0'||a>'9');  
    ans=a-'0';  
    while((a=getchar())>='0'&&a<='9')  
    {  
        ans=ans*10+a-'0';  
    }  
    return ans;  
}  
  
int solve(int l, int r)  
{  
    if(l == r) return a[l];  
    int i = l, j = r, temp = a[l];  
    if(l < r)  
    {  
        while(i < j)  
        {  
            while(i < j && a[j] < temp) j--;  
            if(i < j) a[i++] = a[j];  
            while(i < j && a[i] > temp) i++;  
            if(i < j) a[j--] = a[i];  
        }  
        a[i] = temp;  
        if(i == k) return a[i];  
        else if(i < k)  solve(i+1,r);  
        else solve(l,i-1);  
    }  
}  
  
int main()  
{  
    #ifdef xxz  
    freopen("in.txt","r",stdin);  
    #endif // xxz  
    n = input();  
    k = input();  
    for(int i = 1; i <= n; i++) a[i] = input();  
    printf("%d\n",solve(1,n));  
    return 0;  
}  

利用STL实现
#include<iostream>  
#include <algorithm>  
#include<cstring>  
#include<cstdlib>  
#include<queue>  
#include<cstdio>  
using namespace std;  
const int N = 11111111;  
 void scan_d(int &ret)  
{  
    char c;  
    ret = 0;  
    while((c=getchar())<'0' || c>'9');  
    while(c>='0'&&c<='9') ret = ret*10 +(c-'0'),c=getchar();  
}  
int a[N];  
int main()  
{  
    int n,k;  
    cin>>n>>k;  
    for(int i = 0 ; i<n;i++)  
    {  
           scan_d(a[i]);  
    }  
   
    nth_element(a,a+n-k,a+n);  
    cout<<a[n-k]<<endl;  
}  

Problem B: 拯救花千骨

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 12  Solved: 6
[Submit][Status][Web Board] [Edit] [TestData]

Description

七杀派妖族单春秋又一次围攻蜀山派,花千骨向白子画求救,可是白子画和圣君单挑身受重伤无法支援,他只能让其余长留剑仙赶去支援。

技术分享

 那么现在问题来了:每位长留剑仙都有一个法力值power,每一把仙剑有个启动值weghit,如果长留剑仙想拿起仙剑,那么他的法力值必须大于等于该把仙剑的启动值,现在白子画派出n位长留剑仙,给他们n把仙剑,请问这些剑仙与仙剑总共有多少种搭配方式

技术分享

Input

第一行输入给的测试样例的个数T(1<=T<=50)
第二行输入n(1<=n<=10^5),代表有n位剑仙和n把仙剑
第三行输入n把仙剑的启动值
第四行输入n位剑仙的法力值

Output

对于每个样例,输出"Case #x: ",后面跟上搭配方式的种数
这个数字也许很大,答案要模10^9+7

Sample Input

3
5
1 2 3 4 5
1 2 3 4 5
2
1 3
2 2
3
2 3 4
6 3 5

Sample Output

Case #1: 1
Case #2: 0
Case #3: 4
这题最容易想到的就是暴力o(n*n),会TLE。。然后想了想这里的数据n=10^5,一般情况下1s的限制时间o(n)的算法n最大最大不超过10^9。。所以你n*n=10^10,大概是10s,所以这个算法不行,那么怎么优化呢?一般情况下这种无序的l两次循环扫描需要o(n*m),但是如果事先排好序的话,可以优化到o(n+m),这里3s的时间可以妥妥的过.然后用组合原理轻松搞定

时间复杂度分析

首先同时将两个数组power,weight从小到大排序,然后从扫描power数组,遇到power[i] < weight[j]就i++,否则j++,那么此时j-i就是该power可以拿起weight种类的个数,然后因为power是从大到小排列的,所以不需要回溯(如果不明白的可以仔细想想),所以每次j不需要初始化为0,所以总共扫描n+m次,故时间复杂度是o(n+m)约等于o(n),所以咱就可以ac了~~

#include <iostream>  
#include <algorithm>  
#include <vector>  
#include <cstdio>  
using namespace std;  
typedef long long LL;  
const int Mod = 1000000000+7;  
const int Max = 100000+5;  
int  weight[Max],power[Max];  
int main()  
{  
    int T;  
    scanf("%d",&T);  
    for(int k = 1; k <= T; k++)  
    {  
        int n;  
        scanf("%d",&n);  
        for(int i = 0; i < n; i++)  
        {  
            scanf("%d",&weight[i]);  
        }  
   
        for(int i = 0; i < n; i++)  
        {  
            scanf("%d",&power[i]);  
        }  
   
        sort(power,power+n);  
        sort(weight,weight+n);  
        LL ans = 1;  
         for (int i = 0, j = 0; i < n; i++)  
        {  
            for ( ; j < n; j++)  
                if (weight[j] > power[i])  
                    break;  
            ans = ans * (j-i) % Mod;  
            if (ans == 0) break;  
        }  
        cout << "Case #" << k << ": " << ans << endl;  
    }  
    return 0;  
}  

Problem C: 高数签到题

Time Limit: 2 Sec  Memory Limit: 128 MB
Submit: 41  Solved: 23
[Submit][Status][Web Board] [Edit] [TestData]

Description

一重积分,想到买菜时可算南瓜切开的截面;二重积分,想到买菜时可算冬瓜的个头,;三重积分,想到买菜时可算芹菜的质量;对弧长的曲线积分,想到可算黄瓜的重量;对坐标的曲线积分,要用来算什么?奔跑中的西红柿所做的功!
大家都知道n重积分都是建立在一重积分的问题上,现在lcm老师为了巩固同学们的基础知识,留了n道求一重积分的题目,但是同学们都嫌题目太多,不愿手算,聪明的你能帮助同学们利用计算机快速求出一重积分吗?

假设一个多项式包含n个非零项
其中利用k[i],e[i] 分别表示第i项的系数和指数
比如f(x) = 2x^4 + 3x^5;
则k[1] = 2, e[1] =4
K[2] = 3, e[2] = 5;
系数不是从小到到给出,输出也不需要从小到到输出,按照输入的顺序直接输出积分得到的系数和指数即可
注意:
1:如果系数是整数直接打印出
2:如果系数是非整数,以a/b(必须是最简分数,比如8/16要化为1/2)的形式输出

Input

有多组数据
第一行输入n,代表多项式有n项
第二行输入,n*2整数,代表k[1],e[1],k[2],e[2].....k[n],e[n].
1 ≤ n ≤ 1000
-1000 ≤ k[i] ≤ 1000, k[i] != 0, 1 ≤ i ≤ n
0≤ e[i] ≤ 1000, 1 ≤ i ≤ n

Output

按照题目要求求出一重积分后的每项的系数和指数
注意最后一个指数后面并没有空格

Sample Input

3
1 0 3 2 2 4

Sample Output

1 1 1 3 2/5 5

HINT

f(x) = 1 + 3x^2 + 2x^4


After integrating we get: ∫f(x)dx = x + x^3 + (2/5)x^5


简单求微积分,利用gcd求系数,比如9x^5,那么系数应该是9/6,但是你不能直接除!如果它不整出的话,就逗比了。。题目要求如果不能整出用a/b约分后的形式表示,那么怎么约分呢?当然除gcd 就可以了~~
#include <iostream>  
#include <cstdio>  
using namespace std;  
   
int gcd(int n ,int m)  
{  
    return m == 0 ? n:gcd(m,n%m);  
}  
   
int main()  
{  
  #ifdef xxz  
  freopen("in","r",stdin);  
  #endif // xxz  
  int n;  
  while(cin>>n)  
  {  
      int k,e;  
      for(int i = 1; i < n; i++)  
      {  
          cin>>k>>e;  
          if(k % (e+1) == 0) cout<<k/(e+1)<<" "<<e+1<<" ";  
          else {  
            int g = gcd(k,e+1);  
            if(g > 0) cout<<k/g<<"/"<<(e+1)/g<<" "<<e+1<<" ";  
            else cout<<-k/g<<"/"<<-(e+1)/g<<" "<<e+1<<" ";//主要如果最大公约数小于0的话特殊处理一下  
          }  
      }  
   
        cin>>k>>e;  
          if(k % (e+1) == 0) cout<<k/(e+1)<<" "<<e+1;  
          else {  
            int g = gcd(k,e+1);  
            if(g > 0) cout<<k/g<<"/"<<(e+1)/g<<" "<<e+1;  
            else cout<<-k/g<<"/"<<-(e+1)/g<<" "<<e+1;  
          }  
          cout<<endl;  
   
  }  
    return 0;  
} 


Problem D: R2D2 and Droid Army

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 1  Solved: 1
[Submit][Status][Web Board] [Edit] [TestData]

Description

An army of n droids is lined up in one row. Each droid is described by m integers a1,?a2,?...,?am, where ai is the number of details of thei-th type in this droid‘s mechanism. R2-D2 wants to destroy the sequence of consecutive droids of maximum length. He has m weapons, the i-th weapon can affect all the droids in the army by destroying one detail of the i-th type (if the droid doesn‘t have details of this type, nothing happens to it).
A droid is considered to be destroyed when all of its details are destroyed. R2-D2 can make at most k shots. How many shots from the weapon of what type should R2-D2 make to destroy the sequence of consecutive droids of maximum length?

Input

The first line contains three integers n,?m,?k (1?≤?n?≤?10^5, 1?≤?m?≤?5, 0?≤?k?≤?10^9) — the number of droids, the number of detail types and the number of available shots, respectively.

Next n lines follow describing the droids. Each line contains m integers a1,?a2,?...,?am (0?≤?ai?≤?108), where ai is the number of details of the i-th type for the respective robot.

Output

Print m space-separated integers, where the i-th number is the number of shots from the weapon of the i-th type that the robot should make to destroy the subsequence of consecutive droids of the maximum length.
If there are multiple optimal solutions, print any of them.
It is not necessary to make exactly k shots, the number of shots can be less.

Sample Input

5 2 4
4 0
1 2
2 1
0 2
1 3

Sample Output

2 2

HINT

n the first test the second, third and fourth droids will be destroyed.

In the second test the first and second droids will be destroyed.


这道题目需要特别判断,我忘记加了,如果要再做一遍可以去CF提交,题目来源:Codeforces Round #291 (Div. 2)(D)


题意:有n个机器人,每个机器人有m种部件组成,每种部件可以有多个,有m种抢,每种抢发射一次能是所有机器人对应的部件减1,总共不得发射超过k次,求在伤害机器人数目最大(机器人要求连续)的情况下每种抢发射的最少次数。

思路:线段树+二分,线段树用来求区间最大值,先二分出答案(连续伤害的机器人数),然后枚举起点,知道起点,有范围就知道了终点,然后求这范围内需要设计的最大数目看满不满足条件
#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cstring>  
using namespace std;  
const int MAXNODE = 400000+1000;  
const int MAX = 1000003;  
struct NODE  
{  
    int value[6];        // 结点对应区间的权值  
    int left,right;   // 区间 [left,right]  
} node[MAXNODE];  
int father[MAX];     // 每个点(当区间长度为0时,对应一个点)对应的结构体数组下标  
  
int a[MAXNODE][6];  
int n,m,k;  
int temp[6],ans[6];  
  
void BuildTree(int rt,int left,int right)  
{  
    node[rt].left = left;  
    node[rt].right = right;  
    if (left == right)  
    {  
        for(int i = 1; i <= m; i++ )  
        {  
            node[rt].value[i] = a[left][i];  
        }  
        return;  
  
    }  
    int mid = (right+left)/2;  
    BuildTree(rt<<1, left, mid );  
    BuildTree(rt<<1|1, mid + 1, right);  
  
    for(int i = 1; i <= m; i++)  
    {  
        node[rt].value[i] = max(node[rt<<1].value[i], node[rt<<1|1].value[i]);  
    }  
}  
  
  
  
  
  
int  Query(int i,int l,int r,int x)  // i为区间的序号(对应的区间是最大范围的那个区间,也是第一个图最顶端的区间,一般初始是 1 啦)  
{  
    if (l== node[i].left  && node[i].right == r)  // 找到了一个完全重合的区间  
    {  
        return  node[i].value[x];  
    }  
    int Max = 0;  
    i = i << 1; // get the left child of the tree node  
    if (l <= node[i].right)  // 左区间有涉及  
    {  
        if (r <= node[i].right) // 全包含于左区间,则查询区间形态不变  
            Max = Query(i, l, r,x);  
        else // 半包含于左区间,则查询区间拆分,左端点不变,右端点变为左孩子的右区间端点  
            Max =Query(i,l, node[i].right,x);  
    }  
    i += 1; // right child of the tree  
    if (r >= node[i].left)  // 右区间有涉及  
    {  
        if (l >= node[i].left) // 全包含于右区间,则查询区间形态不变  
            Max =max(Max, Query(i, l, r,x));  
        else // 半包含于左区间,则查询区间拆分,与上同理  
            Max =max(Max, Query(i, node[i].left, r,x));  
    }  
  
    return Max;  
}  
  
bool ok(int mid)  
{  
    bool flag = false;  
    for(int i = 1; i <= n - mid+1; i++)  
    {  
        int num = 0;  
        //cout<<"i = "<<i;  
        for(int j = 1; j <= m; j++)  
        {  
            temp[j] = Query(1,i, i+mid-1 , j);  
            num += temp[j];  
            //cout<<temp[j]<<" ";  
        }  
        // cout<<endl;  
        if(num <= k)  
        {  
            flag = true;  
            memcpy(ans,temp,sizeof(temp));  
            // cout<<i<<" "<<ans[1]<<" "<<ans[2]<<endl;  
            break;  
        }  
  
        fill(temp,temp+6,0);  
    }  
  
    if(flag) return true;  
    else return false;  
}  
void solve()  
{  
    int left = 1, right = n;  
  
    while(left <= right)  
    {  
        int mid = (left + right)/2;  
        // cout<<mid<<endl;  
  
        if(ok(mid))  
        {  
            left = mid + 1;  
        }  
        else right = mid - 1;  
    }  
  
    printf("%d",ans[1]);  
    for(int i = 2; i <= m; i++) printf("% d",ans[i]);  
    printf("\n");  
}  
  
int main()  
{  
#ifdef xxz  
    freopen("in.txt","r",stdin);  
#endif // xxz  
    while(scanf("%d%d%d",&n,&m,&k) != EOF)  
    {  
        for(int i = 1; i <= n; i++)  
            for(int j = 1; j <= m; j++)  
                scanf("%d",&a[i][j]);  
  
        BuildTree(1,1,n);  
  
        solve();  
    }  
    return 0;  
} 

Problem E: Inverse the Problem

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 8  Solved: 2
[Submit][Status][Web Board] [Edit] [TestData]

Description

There is an easy way to obtain a new task from an old one called "Inverse the problem": we give an output of the original task, and ask to generate an input, such that solution to the original problem will produce the output we provided. The hard task of Topcoder Open 2014 Round 2C, InverseRMQ, is a good example.
Now let‘s create a task this way. We will use the task: you are given a tree, please calculate the distance between any pair of its nodes. Yes, it is very easy, but the inverse version is a bit harder: you are given an n?×?n distance matrix. Determine if it is the distance matrix of a weighted tree (all weights must be positive integers).

Input

The first line contains an integer n (1?≤?n?≤?2000) — the number of nodes in that graph.
Then next n lines each contains n integers di,?j (0?≤?di,?j?≤?10^9) — the distance between node i and node j.

Output

f there exists such a tree, output "YES", otherwise output "NO".

Sample Input

3
0 2 7
2 0 9
7 9 0
3
1 2 7
2 0 9
7 9 0
3
0 2 2
7 0 9
7 9 0
3
0 1 1
1 0 1
1 1 0

Sample Output

YES
NO
NO
NO

HINT

In the first example, the required tree exists. It has one edge between nodes 1 and 2 with weight 2, another edge between nodes 1 and 3 with weight 7.

In the second example, it is impossible because d1,?1 should be 0, but it is 1.

In the third example, it is impossible because d1,?2 should equal d2,?1.


给定一个矩阵,表示每两个节点之间的权值距离,问是否可以对应生成一棵树,
使得这棵树中的任意两点之间的距离和矩阵中的对应两点的距离相等!

思路:我们将给定的矩阵看成是一个图,a 到 b会有多条路径, 如果存在一棵树,那么
这个树中a->b的距离一定是这个图中所有a->b中路径长度最短的一条!所以我们根据边权,
建立一棵MST树!再将MST树中的任意两点之间的距离求出来,看是否和矩阵中的对应的节点
对距离相同,
我们首先构造一个最小生成树,然后比较各各点之间的距离是否与题目给出的距离相等,可以用dfs搜索整张图的每两个点之间的距离.下面给的做法非dfs做的,用一个数组f[][],记录x,y两点之间的距离,算距离的时候是通过目前点的前驱找,也就是说需要一个数组记录前驱,这样就可以不用dfs了,直接可以算.看完代码就明白了.....

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <algorithm>  
using namespace std;  
const int maxn = 2010;  
const int INF = 0x3f3f3f3f;  
int graph[maxn][maxn];  
int prior[maxn];  
int visit[maxn];  
int dis[maxn];  
int f[maxn][maxn];  
int n;  
bool check()  
{  
    for(int i = 0; i < n; i++)  
    {  
        dis[i] = INF;  
        if(graph[i][i] != 0) return false;  
        for(int j = i+1 ; j < n; j++)  
        {  
            if(graph[i][j] != graph[j][i] || graph[i][j] == 0) return false;  
        }  
    }  
  
    memset(visit,0,sizeof(visit));  
    memset(prior,-1,sizeof(prior));  
    memset(f,0,sizeof(f));  
    int cent = n;  
    dis[0]=0;  
    while(cent--)//循环n次是因为要初始化  
    {  
  
        int min = -1;  
        for(int i = 0; i < n; i++)  
        {  
            if(!visit[i] && (min == -1 || dis[i] < dis[min]))  
            {  
                min = i;  
            }  
        }  
        for(int i = 0; i < n; i++)//在prim算法里面增加这层循环里面的内容算距离  
        {  
            if(visit[i])//必须是已经访问过的点,才能算距离  
            {  
                f[i][min] = f[min][i] = f[i][prior[min]] + dis[min];  
            }  
        }  
        visit[min] = true;  
        for(int i = 0; i < n; i++)  
        {  
            if(dis[i] > graph[min][i] )  
            {  
                dis[i] = graph[min][i];  
                prior[i] = min;//记录前驱  
            }  
        }  
    }  
  
    for(int i = 0; i < n; i++)  
    {  
        for(int j = 0 ; j < n; j++)  
        {  
            if(f[i][j] != graph[i][j])  
            {  
                return false;  
            }  
        }  
    }  
      return true;  
}  
int main()  
{  
#ifdef xxz  
    freopen("in","r",stdin);  
#endif // xxz  
    while(cin>>n)  
    {  
        for(int i = 0; i < n; i++)  
            for(int j = 0; j < n; j++)  
            {  
                cin>>graph[i][j];  
            }  
  
        if(check()) cout<<"YES"<<endl;  
        else cout<<"NO"<<endl;  
    }  
    return 0;  
} 


然后非常抱歉,我出数据的时候没出好,rejudge了两次,非常抱歉,大家期末考试加油吧~~~

HLJU周赛5解题报告

标签:

原文地址:http://blog.csdn.net/u013445530/article/details/46626565

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!