第八次新生排位赛

第一题

主要是判断一条边的两个角是锐角还是钝角,只要有一个是钝角这条边朝下就不会掉下来

关于判断钝角最好用点积,避免了很多不必要的精度麻烦

时间限制 1000 ms  内存限制 65536 KB

题目描述

小弱发明了一个好玩的游戏,游戏开始时给你一个凸多边形,这个多边形可以任意旋转,平移。让你造出一个正方形“容器”(足够大),容器的两边垂直,两边水平。用这个容器把这个多边形完全包含,且多边形有且只有一条边恰好与容器下边界重合(与外界相通),不同的边与外界相通代表不同的方案。现在让你判断是否有方案可以让这个多边形能够不掉下来。不掉下来要求是至少有一条边与容器之间存在压力(假设摩擦系数无穷大)。
如下图,左边会掉下来,右边不会掉下来。

输入格式

有多组数据。
对于每组数据,第一行是多边形点数n(3 <= n <= 1000),后面n行分别是这些点沿着多边形逆时针的二维坐标(xi, yi)(xi,yi的绝对值 <= 1000,输入数据精度精确到两位)。

输出格式

对每组数据输出有多少种方案可以使多边形不掉下来。

输入样例

3
0.00 0.00
2.00 0.00
1.00 1.00
3 
0.00 0.00
1.00 0.00
2.00 1.00
4
0.00 0.00
4.00 0.00
3.00 2.00
1.00 2.00

输出样例

0
2
3
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <cmath>
#include <algorithm>
 
using namespace std;
struct database
{
    double x, y;
} a[1005];
bool b[1005];
int cmp(database mid, database pre, database next)
{
    if (((pre.x-mid.x)*(next.x-mid.x)+(pre.y-mid.y)*(next.y-mid.y)) < 0) return true;
    else return 0;
}
 
int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        for (int i=0; i<n; i++)
        {
            scanf("%lf %lf", &a[i].x, &a[i].y);
        }
        for (int i=1; i<n-1; i++)
            b[i] = cmp(a[i], a[i-1], a[i+1]);
        b[0] = cmp(a[0], a[n-1], a[1]);
        b[n-1] = cmp(a[n-1], a[n-2], a[0]);
        int ans = 0;
        for (int i=0; i<n-1; i++)
        {
            if (b[i]||b[i+1])
                ans++;
        }
        if (b[0]||b[n-1])
            ans++;
        printf("%d\n", ans);
 
    }
    return 0;
}


第二题

时间限制 1000 ms  内存限制 65536 KB

题目描述

Mays王国的女王大人每天过着自由自在的生活,她最大的乐趣就是给邻国的帅气王子写信。但是最近,Mays王国的叔叔们变得很无聊,他们知道女王大人每次都把信委托给皇家小妹妹快递公司的小妹妹们,于是叔叔们给每一条路都设立了路障,只有小妹妹们给他们表演节目才会让小妹妹们过去。
在每一个路障,都有不同数量的叔叔,只有表演的小妹妹的数量不少与叔叔的数量的时候叔叔才会放她们过去。
为了节省开销,小妹妹快递公司希望派最少的小妹妹把女王大人的信件送到。请你告诉他们需要派几个小妹妹。

输入格式

输入第一行为数据组数T(T<=10),接下来T组数据,每组第一行为n,m,,2<=n<=10000,1<=m<=100000,表示Mays王国的道路由n个节点组成,接下来m行,每行一组u,v,c表示连接节点u,v的一条无向道路,且路障上有c个叔叔,1<=u,v<=n,0<=c<=100。女王大人和皇家小妹妹快递公司都在节点1,帅气的邻国王子住在节点n。

输出格式

每组数据输出一个数字,表示小妹妹快递公司最少需要派出的小妹妹数量。如果无论派出多少小妹妹都无法把信送到帅气的邻国王子手里,输出"shimatta!"。

输入样例

1
3 3
1 2 1
2 3 1
1 3 3

输出样例

1
这道题目前有三种做法,spfa, 并查集, 二分枚举

但是这些都不是关键,最重要的是当路上一个怪叔叔都没有的时候还是需要有一个小妹妹去送信,而不是0个


spfa 这里主要注意一下,a[i]记录从1到i点至少需要小妹妹的个数。对于一条u到v的边,取a[u]与边的权值中的较大者,与a[v]比较,再取两者中的较小者,这里比较绕,需要好好理解一下


#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstring>
  
using namespace std;
  
struct
{
    int to, next, value;
}a[200005];
  
int head[10005];
bool check[10005];
int ans[10005];
int tot;
  
void add(int u, int v, int val)
{
    a[tot].to = v;
    a[tot].next = head[u];
    a[tot].value = val;
    head[u] = tot++;
}
queue <int> que;
void spfa()
{
   while (!que.empty())
        que.pop();
   que.push(1);
   check[1] = true;
   ans[1] = 0;
   int temp;
   while (!que.empty())
   {
       temp = que.front();
       que.pop();
       check[temp] = 0;
       for (int k=head[temp]; k!=-1; k=a[k].next)
       {
           int p = a[k].to;
           int pet = max(ans[temp], a[k].value);
           if (ans[p]>pet)
           {
               ans[p] = pet;
               if (!check[p])
               {
                   que.push(p);
                   check[p] = true;
               }
           }
       }
   }
}
  
  
int main()
{
    int t;
    scanf("%d", &t);
    for (int i=0; i<t; i++)
    {
        int n, m;
        int u, v, val;
        scanf("%d %d", &n, &m);
  
        for (int s=0; s<=n; s++)
        {
           head[s] = -1;
           ans[s] = 150;
           check[s] = 0;
        }
        for (int j=0; j<m; j++)
        {
            scanf("%d %d %d", &u, &v, &val);
            add(u, v, val);
            add(v, u, val);
        }
        tot = 0;
        spfa();
        if (ans[n]==150)
            printf("shimatta!\n");
        else if (ans[n]==0)
            printf("1\n");
        else
            printf("%d\n", ans[n]);
  
    }
  
    return 0;
}


并查集

关于两个本来不在同一集合的两个点,到底谁做父亲,谁做孩子,这个是有讲究的,对于这题来说,加了一句

father[max(u, v)] = min(u, v);

就从超时变成了顺利通过


#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstring>
 
using namespace std;
 
struct data
{
    int value, u, v;
} a[100005];
int father[10005];
bool cmp(data a1, data a2)
{
    return a1.value < a2.value;
}
 
int check(int u, int v)
{
    while (father[u] != u)
       u = father[u];
    while (father[v] != v)
       v = father[v];
    if (u!=v)
        return 0;
    else return 1;
}
 
void merg(int u, int v)
{
    while (father[u] != u)
       u = father[u];
    while (father[v] != v)
       v = father[v];
 
    father[max(u, v)] =min(u, v);
}
int main()
{
    int t;
    scanf("%d", &t);
    for (int j=0; j<t; j++)
    {
        int n, m;
        scanf("%d %d", &n, &m);
        for (int i=0; i<m; i++)
        {
            scanf("%d %d %d", &a[i].u, &a[i].v, &a[i].value);
        }
        sort(a, a+m, cmp);
        for (int i=0; i<=n; i++)
            father[i] = i;
        int flag = 0;
        int ans;
        for (int i=0; i<m; i++)
        {
            merg(a[i].u, a[i].v);
            if (check(1, n))
            {
                flag = 1;
                ans = a[i].value;
                break;
            }
        }
        if (flag&&(ans == 0))
            printf("1\n");
        else if (flag)
            printf("%d\n", ans);
        else
            printf("shimatta!\n");
 
    }
 
    return 0;
}

二分枚举 这个方法很奇妙,虽然不知道对于其他题效果怎么样,不过对于这道确实是非常快的,不过这个显然不是我的代码

(by taocp)


#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
int n,m;
int head[11111];
bool vis[11111];
struct EDGE{
    int from,to,v,nxt;
}ed[222222];
int que1[11111];
bool bfs(int s,int limit){
    int headx=0,tail=0;
    vis[s]=true;
    que1[tail++]=s;
    while(headx<tail){
        int now=que1[headx++];
        if (now==n)
            return true;
        for(int id=head[now];id!=-1;id=ed[id].nxt){
            int to=ed[id].to;
            if(!vis[to] && ed[id].v<=limit){
                vis[to]=true;
                que1[tail++]=to;
            }
        }
    }
    return false;
}
int main(){
    #ifndef ONLINE_JUDGE
        freopen("D:/in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    int T;scanf("%d",&T);
    while(T--){
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        int maxn=0;
        for(int i=0;i<2*m;){
            int from,to,v;scanf("%d%d%d",&from,&to,&v);
            maxn=max(maxn,v);
            ed[i].from=from;
            ed[i].to=to;
            ed[i].v=v;
            ed[i].nxt=head[from];
            head[from]=i++;
            ed[i].from=to;
            ed[i].to=from;
            ed[i].v=v;
            ed[i].nxt=head[to];
            head[to]=i++;
        }
        int l=0,r=maxn;
        int mid=(l+r)/2;
        memset(vis,0,sizeof(vis));
        if(bfs(1,maxn)==false){
            printf("shimatta!\n");
            continue;
        }
        while(l<r){
            memset(vis,0,sizeof(vis));
            if(bfs(1,mid)){
                r=mid;
            }else{
                l=mid+1;
            }
            mid=(l+r)/2;
        }
        if(mid==0)
            mid++;
        printf("%d\n",mid);
    }
}

第三题

时间限制 1000 ms  内存限制 10000 KB

题目描述

学姐辛辛苦苦准备了一次讲座,讲课的过程中学姐数了一下人数,发现居然少到了一个学弟,学姐很生气,下决心要把这个学弟找出来。学姐开始点名了,为了快一点签到,学姐点名的时候只点大家的学号。学姐说:“这么简单的统计,几行就可以搞定,帮我写个程序吧,超过500B的运行时间又在1ms以上的我不要”。

oj的功能出了点问题,内存判定没有效果,代码长度限制也没有效果。oj上显示超过528B的代码,比赛结束前时限直接调成1ms手工重新判定。(换句话说,本地代码大小超过500B的AC不算。)

输入格式

每组数据第一行一个数N,接下来N-1行每行一个数字表示学弟的学号。

多组数据,eof结束。

2组数据N为1000000

500组数据满足N不大于3000

1000组数据满足N不大于10

 

输出格式

输出没到的学弟的学号

输入样例

3
1 
3
2
1

输出样例

2
2
这题主要是代码长度有要求,所以头文件什么的,该减的一个不要留。

思路分为两种,一个是求得n*(n+1)/2, 然后再一个个减 ,减到最后就是没到的那个人的序号

另一种,先求1到N的异或x,再求已知学号的异或y,x = y^z <==> x^y = z;

所以用x^y, 就可以得到所求学号 不知道为什么我用c做的代码过不了

#include <iostream>
#include <cstdio>
#include <cmath>
 
using namespace std;
long long t;
int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        t = n;
        t = (t+1)*t/2;
        int tmp;
        for (int i=1; i<n; i++)
        {
            scanf("%d", &tmp);
            t -= tmp;
        }
        printf("%lld\n", t);
    }
    return 0;
}

#include <iostream>
#include <cstdio>
 
using namespace std;
 
int main()
{
    int n, x, y, tmp;
    while(~scanf("%d", &n))
    {
        x = n;
        y = 0;
        while (--n)
        {
            x ^= n;
            scanf("%d", &tmp);
            y ^= tmp;
        }
        printf("%d\n", x^y);
    }
    return 0;
}

第四题 据说有个很好听的名字叫概率dp 2的几次方用二进制表示比较准确,pow的结果是double类的

但是位运算符的优先性很搞不清,又被坑了

时间限制 1000 ms  内存限制 65536 KB

题目描述

明光村迎来了一年一度的盛世——解码锦标赛,有 2^N 次个队伍从世界各村赶来参与比赛,编号为 1 - 2^N。赛制为每一轮晋级一半队伍,按序号大小两两比赛,淘汰弱者。一轮结束后,所有的胜者进入下一轮,依旧是按顺序两两比赛。比如第一轮就是 1 vs 2, 3 vs 4 ... 2^N - 1 vs 2^N。在一旁围观的 Mays 学姐告诉你,N次比赛后的胜者是唯一的。现在你拿到了一份各个参赛队伍的对抗胜率表 win,为 2^N * 2^N 的矩阵, win[i][j] 为一位小数,代表i胜j的概率。 

你能告诉 Mays 学姐最有可能获得世界冠军的是那支队伍吗?

输入格式

多组数据。每组第一行为 N ,N <= 8,接下来 N 行 N 列为对抗胜率矩阵。 保证 win[i][j] + win[j][i] = 1 (i != j)。 以 N=0 结束输入。 

输出格式

对每组数据,输出胜率最大的队伍的序号。如果最大的两个概率相差不到 0.001,则认为胜率相等,输出序号最小者。

输入样例

2
0.0 0.1 0.2 0.3
0.9 0.0 0.4 0.5
0.8 0.6 0.0 0.6
0.7 0.5 0.4 0.0
2
0.0 0.8 0.1 0.4 
0.2 0.0 0.2 0.6 
0.9 0.8 0.0 0.3 
0.6 0.4 0.7 0.0 
0

输出样例

2
4
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstring>
 
using namespace std;
 
long double win[550][550];
long double a[10][550];
long double maxn;
int main()
{
    int n;
    scanf("%d", &n);
    while (n)
    {
        for (int i=1; i<=(1<<n); i++)
            for (int j=1; j<=(1<<n); j++)
            {
                scanf("%Lf", &win[i][j]);
            }
        for (int i=1; i<=(1<<n); i++)
            a[0][i] = 1;
        for (int i=1; i<=n; i++)
            for (int j=1; j<=(1<<n); j++)
            {
                a[i][j] = 0;
                int temp = ((int)(j-1)/(1<<i))*(1<<i);
                if (((j-1)%(1<<i)) >= (1<<(i-1)))
                {
                    for (int k = (temp+1); k<=(temp+(1<<(i-1))); k++)
                        a[i][j] += a[i-1][k]*win[j][k];
                }
                else
                {
                    for (int k=(temp+(1<<(i-1))+1); k<=(temp+(1<<i)); k++)
                        a[i][j] += a[i-1][k]*win[j][k];
                }
                a[i][j] *= a[i-1][j];
            }
        maxn = a[n][1];
        int anst = 1;
        for (int i=2; i<=(1<<n); i++)
        {
            if (a[n][i]>=(maxn+0.001-1e-8))
            {
                maxn = a[n][i];
                anst = i;
            }
        }
        printf("%d\n", anst);
        scanf("%d", &n);
    }
 
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值