2018年1月26日天梯赛练习1

7-1 寻找250(10 分)

对方不想和你说话,并向你扔了一串数…… 而你必须从这一串数字中找到“250”这个高大上的感人数字。

输入格式:

输入在一行中给出不知道多少个绝对值不超过1000的整数,其中保证至少存在一个“250”。

输出格式:

在一行中输出第一次出现的“250”是对方扔过来的第几个数字(计数从1开始)。题目保证输出的数字在整型范围内。

输入样例:

888 666 123 -233 250 13 250 -222

输出样例:

5
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int main()
{
    int n,f=0,i=1;
    while(~scanf("%d",&n))
    {
        if(n==250&&!f)f=i;
        i++;
    }
    printf("%d",f);
    return 0;
}

7-2 日期格式化(5 分)

世界上不同国家有不同的写日期的习惯。比如美国人习惯写成“月-日-年”,而中国人习惯写成“年-月-日”。下面请你写个程序,自动把读入的美国格式的日期改写成中国习惯的日期。

输入格式:

输入在一行中按照“mm-dd-yyyy”的格式给出月、日、年。题目保证给出的日期是1900年元旦至今合法的日期。

输出格式:

在一行中按照“yyyy-mm-dd”的格式给出年、月、日。

输入样例:

03-15-2017

输出样例:

2017-03-15
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int main()
{
   int y,m,d;
   scanf("%d-%d-%d",&m,&d,&y);
   printf("%d-%02d-%02d",y,m,d);
    return 0;
}

7-3 阅览室(20 分)

天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时,管理员输入书号并按下S键,程序开始计时;当读者还书时,管理员输入书号并按下E键,程序结束计时。书号为不超过1000的正整数。当管理员将0作为书号输入时,表示一天工作结束,你的程序应输出当天的读者借书次数和平均阅读时间。

注意:由于线路偶尔会有故障,可能出现不完整的纪录,即只有S没有E,或者只有E没有S的纪录,系统应能自动忽略这种无效纪录。另外,题目保证书号是书的唯一标识,同一本书在任何时间区间内只可能被一位读者借阅。

输入格式:

输入在第一行给出一个正整数N(10),随后给出N天的纪录。每天的纪录由若干次借阅操作组成,每次操作占一行,格式为:

书号([1, 1000]内的整数) 键值SE) 发生时间hh:mm,其中hh是[0,23]内的整数,mm是[0, 59]内整数)

每一天的纪录保证按时间递增的顺序给出。

输出格式:

对每天的纪录,在一行中输出当天的读者借书次数和平均阅读时间(以分钟为单位的精确到个位的整数时间)。

输入样例:

3
1 S 08:10
2 S 08:35
1 E 10:00
2 E 13:16
0 S 17:00
0 S 17:00
3 E 08:10
1 S 08:20
2 S 09:00
1 E 09:20
0 E 17:00

输出样例:

2 196
0 0
1 60
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        double t=0;
        int s=0,x,p,q;
        map<int,char>a;
        map<int,int>b;
        char m;
        while(scanf("%d %c%d:%d",&x,&m,&p,&q),x)
        {
            if(m=='S')
                a[x]='S',b[x]=p*60+q;
            else if(a[x]=='S')
                t+=p*60+q-b[x],s++,a[x]='0';
        }
        if(s)t/=s;
        printf("%d %d
",s,(int)(t+0.5));
    }
    return 0;
}

7-4 稳赢(15 分)

大家应该都会玩“锤子剪刀布”的游戏:两人同时给出手势,胜负规则如图所示:

现要求你编写一个稳赢不输的程序,根据对方的出招,给出对应的赢招。但是!为了不让对方输得太惨,你需要每隔K次就让一个平局。

输入格式:

输入首先在第一行给出正整数K(10),即平局间隔的次数。随后每行给出对方的一次出招:ChuiZi代表“锤子”、JianDao代表“剪刀”、Bu代表“布”。End代表输入结束,这一行不要作为出招处理。

输出格式:

对每一个输入的出招,按要求输出稳赢或平局的招式。每招占一行。

输入样例:

2
ChuiZi
JianDao
Bu
JianDao
Bu
ChuiZi
ChuiZi
End

输出样例:

Bu
ChuiZi
Bu
ChuiZi
JianDao
ChuiZi
Bu
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

小模拟
#include<bits/stdc++.h>
using namespace std;
map<string,string>M;
int main()
{
    M["ChuiZi"]="Bu",M["Bu"]="JianDao",M["JianDao"]="ChuiZi";
    int n,num=0;
    cin>>n;
    string s;
    while(cin>>s,s!="End")
    {
        num++;
        if(num<=n)
        {
            cout<<M[s]<<"
";
        }
        else
        {
            num=0,cout<<s<<"
";
        }
    }
    return 0;
}

7-5 宇宙无敌大招呼(5 分)

据说所有程序员学习的第一个程序都是在屏幕上输出一句“Hello World”,跟这个世界打个招呼。作为天梯赛中的程序员,你写的程序得高级一点,要能跟任意指定的星球打招呼。

输入格式:

输入在第一行给出一个星球的名字S,是一个由不超过7个英文字母组成的单词,以回车结束。

输出格式:

在一行中输出Hello S,跟输入的S星球打个招呼。

输入样例:

Mars

输出样例:

Hello Mars
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

Hello World题
#include<bits/stdc++.h>
using namespace std;
map<string,string>M;
int main()
{
    string s;
    cin>>s;
    cout<<"Hello "<<s<<"
";
    return 0;
}

7-6 整除光棍(20 分)

这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s,表示x乘以s是一个光棍,第二个数字n是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。

提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除x为止。但难点在于,s可能是个非常大的数 —— 比如,程序输入31,那么就输出3584229390681和15,因为31乘以3584229390681的结果是111111111111111,一共15个1。

输入格式:

输入在一行中给出一个不以5结尾的正奇数x<1000)。

输出格式:

在一行中输出相应的最小的sn,其间以1个空格分隔。

输入样例:

31

输出样例:

3584229390681 15
作者: 翁恺
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

大数模拟
#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    int num=0,n;
    int t=1;
    scanf("%d",&n);
    for(;;)
    {
        num++;
        if(s.length()||t/n)s+='0'+t/n;
        t%=n;
        if(!t)
        {
            cout<<s<<" "<<num<<"
";
            return 0;
        }
        t=t*10+1;
    }
}

7-7 装睡(10 分)

你永远叫不醒一个装睡的人 —— 但是通过分析一个人的呼吸频率和脉搏,你可以发现谁在装睡!医生告诉我们,正常人睡眠时的呼吸频率是每分钟15-20次,脉搏是每分钟50-70次。下面给定一系列人的呼吸频率与脉搏,请你找出他们中间有可能在装睡的人,即至少一项指标不在正常范围内的人。

输入格式:

输入在第一行给出一个正整数N(10)。随后N行,每行给出一个人的名字(仅由英文字母组成的、长度不超过3个字符的串)、其呼吸频率和脉搏(均为不超过100的正整数)。

输出格式:

按照输入顺序检查每个人,如果其至少一项指标不在正常范围内,则输出其名字,每个名字占一行。

输入样例:

4
Amy 15 70
Tom 14 60
Joe 18 50
Zoe 21 71

输出样例:

Tom
Zoe
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        string s;
        int a,b;
        cin>>s>>a>>b;
        if(a<15||a>20||b<50||b>70)
            cout<<s<<"
";
    }
    return 0;
}

7-8 矩阵A乘以B(15 分)

给定两个矩阵A和B,要求你计算它们的乘积矩阵AB。需要注意的是,只有规模匹配的矩阵才可以相乘。即若A有Ra​​行、Ca​​列,B有Rb​​行、Cb​​列,则只有Ca​​与Rb​​相等时,两个矩阵才能相乘。

输入格式:

输入先后给出两个矩阵A和B。对于每个矩阵,首先在一行中给出其行数R和列数C,随后R行,每行给出C个整数,以1个空格分隔,且行首尾没有多余的空格。输入保证两个矩阵的R和C都是正数,并且所有整数的绝对值不超过100。

输出格式:

若输入的两个矩阵的规模是匹配的,则按照输入的格式输出乘积矩阵AB,否则输出Error: Ca != Rb,其中CaA的列数,RbB的行数。

输入样例1:

2 3
1 2 3
4 5 6
3 4
7 8 9 0
-1 -2 -3 -4
5 6 7 8

输出样例1:

2 4
20 22 24 16
53 58 63 28

输入样例2:

3 2
38 26
43 -5
0 17
3 2
-11 57
99 68
81 72

输出样例2:

Error: 2 != 3
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

线代概念题
#include<bits/stdc++.h>
using namespace std;
int A[105][105],B[105][105],C[105][105];
int main()
{
    int a,b;
    cin>>a>>b;
    for(int i=0; i<a; i++)
        for(int j=0; j<b; j++)
            cin>>A[i][j];
    int c,d;
    cin>>c>>d;
    for(int i=0; i<c; i++)
        for(int j=0; j<d; j++)
            cin>>B[i][j];
    if(b!=c)
        cout<<"Error: "<<b<<" != "<<c;
    else
    {
        cout<<a<<" "<<d<<"
";
        for(int i=0; i<a; i++)
            for(int j=0; j<d; j++)
                for(int k=0; k<b; k++)
                    C[i][j]+=A[i][k]*B[k][j];
        for(int i=0; i<a; i++)
        {
            cout<<C[i][0];
            for(int j=1; j<d; j++)
                cout<<" "<<C[i][j];
            cout<<"
";
        }
    }
}

7-9 点赞狂魔(25 分)

微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。然而有这么一种人,他们会通过给自己看到的一切内容点赞来狂刷存在感,这种人就被称为“点赞狂魔”。他们点赞的标签非常分散,无法体现出明显的特性。本题就要求你写个程序,通过统计每个人点赞的不同标签的数量,找出前3名点赞狂魔。

输入格式:

输入在第一行给出一个正整数N(100),是待统计的用户数。随后N行,每行列出一位用户的点赞标签。格式为“Name F1​​FK​​”,其中Name是不超过8个英文小写字母的非空用户名,1K1000,Fi​​(i=1,,K)是特性标签的编号,我们将所有特性标签从1到107编号。数字间以空格分隔。

输出格式:

统计每个人点赞的不同标签的数量,找出数量最大的前3名,在一行中顺序输出他们的用户名,其间以1个空格分隔,且行末不得有多余空格。如果有并列,则输出标签出现次数平均值最小的那个,题目保证这样的用户没有并列。若不足3人,则用-补齐缺失,例如mike jenny -就表示只有2人。

输入样例:

5
bob 11 101 102 103 104 105 106 107 108 108 107 107
peter 8 1 2 3 4 3 2 5 1
chris 12 1 2 3 4 5 6 7 8 9 1 2 3
john 10 8 7 6 5 4 3 2 1 7 5
jack 9 6 7 8 9 10 11 12 13 14

输出样例:

jack chris john
作者: 陈越
单位: 浙江大学
时间限制: 200ms
内存限制: 64MB
代码长度限制: 16KB

#include<bits/stdc++.h>
using namespace std;
struct T
{
    string s;
    int sum,num;
} t ;
int cmp(T a,T b)
{
    return a.sum>b.sum||a.sum==b.sum&&a.num<b.num;
}
int main()
{
    int n;
    cin>>n;
    vector<T>V;
    for(int i=0; i<n; i++)
    {
        set<int>s;
        cin>>t.s;
        cin>>t.num;
        t.sum=0;
        for(int j=0,x; j<t.num; j++)
        {
            cin>>x;
            if(!s.count(x))
                s.insert(x),t.sum++;
        }
        V.push_back(t);
    }
    sort(V.begin(),V.end(),cmp);
    t.s="-";
    while(V.size()<3)V.push_back(t);
    cout<<V[0].s;
    for(int i=1; i<3; i++)
        cout<<" "<<V[i].s;
    return 0;
}

7-10 重排链表(25 分)

给定一个单链表 L1​​→L2​​→⋯→Ln1​​→Ln​​,请编写程序将链表重新排列为 Ln​​→L1​​→Ln1​​→L2​​→⋯。例如:给定L为1→2→3→4→5→6,则输出应该为6→1→5→2→4→3。

输入格式:

每个输入包含1个测试用例。每个测试用例第1行给出第1个结点的地址和结点总个数,即正整数N(105​​)。结点的地址是5位非负整数,NULL地址用1表示。

接下来有N行,每行格式为:

Address Data Next

其中Address是结点地址;Data是该结点保存的数据,为不超过105​​的正整数;Next是下一结点的地址。题目保证给出的链表上至少有两个结点。

输出格式:

对每个测试用例,顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。

输入样例:

00100 6
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218

输出样例:

68237 6 00100
00100 1 99999
99999 5 12309
12309 2 00000
00000 4 33218
33218 3 -1
作者: 陈越
单位: 浙江大学
时间限制: 500ms
内存限制: 64MB
代码长度限制: 16KB

链表模拟,直接找到这个序列就好,我的map没有处理到有多余节点的,也很是奇怪
#include<bits/stdc++.h>
using namespace std;
struct T
{
    int ad,data,nxt;
} t;
map<int,T>M;
vector<T>V;
int main()
{
    int head,n;
    cin>>head>>n;
    for(int i=0; i<n; i++)
    {
        cin>>t.ad>>t.data>>t.nxt;
        M[t.ad]=t;
    }
    int f=head;
    while(f!=-1)
    {
        V.push_back(M[f]);
        head=M[f].ad;
        f=M[f].nxt;
    }
    f=head;
    for(int i=0; i<V.size(); i++)
    {
        printf("%05d ",f);
        if(i==V.size()-1)
        {
            if(i&1)
                printf("%d ",V[i/2].data);
            else printf("%d ",V[n-i/2-1].data);
            printf("-1");
        }
        else if(i&1)
            printf("%d %05d
",V[i/2].data,V[n-i/2-2].ad),f=V[n-i/2-2].ad;
        else printf("%d %05d
",V[n-i/2-1].data,V[i/2].ad),f=V[i/2].ad;

    }
    return 0;
}

数组的

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int key,nxt,ad;
} f[1000005],f1[1000005],f2[1000005];
int main()
{
    int adr,n;
    cin>>adr>>n;
    for(int i=0,a,b,c; i<n; i++)
    {
        cin>>a>>b>>c;
        f[a].key=b;
        f[a].nxt=c;
    }
    int l=0;
    for(int i=adr; i!=-1; i=f[i].nxt)
        f1[l].ad=i,f1[l].key=f[i].key,f1[l++].nxt=f[i].nxt;
    int l1=0;
    if(l%2==0)
        for(int i=l-1,j=0; i>=(l+1)/2; i--,j++)
        {
            if(i!=j)
            {
                f2[l1].ad=f1[i].ad,f2[l1].key=f1[i].key,f2[l1++].nxt=f1[i].nxt;
                f2[l1].ad=f1[j].ad,f2[l1].key=f1[j].key,f2[l1++].nxt=f1[j].nxt;
            }
            else
            {
                f2[l1].ad=f1[i].ad,f2[l1].key=f1[i].key,f2[l1++].nxt=f1[i].nxt;
            }
        }
    else
    {
        for(int i=l-1,j=0; ; i--,j++)
        {
            if(i!=j)
            {
                f2[l1].ad=f1[i].ad,f2[l1].key=f1[i].key,f2[l1++].nxt=f1[i].nxt;
                f2[l1].ad=f1[j].ad,f2[l1].key=f1[j].key,f2[l1++].nxt=f1[j].nxt;
            }
            else
            {
                f2[l1].ad=f1[i].ad,f2[l1].key=f1[i].key,f2[l1++].nxt=f1[i].nxt;
            }
            if(i==j)break;
        }
    }
    for(int i=0; i<l1-1; i++)
        printf("%05d %d %05d
",f2[i].ad,f2[i].key,f2[i+1].ad);
    printf("%05d %d %d
",f2[l1-1].ad,f2[l1-1].key,-1);
}

7-11 图着色问题(25 分)

图着色问题是一个著名的NP完全问题。给定无向图G=(V,E),问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色?

但本题并不是要你解决这个着色问题,而是对给定的一种颜色分配,请你判断这是否是图着色问题的一个解。

输入格式:

输入在第一行给出3个整数V(0<V500)、E(0)和K(0<KV),分别是无向图的顶点数、边数、以及颜色数。顶点和颜色都从1到V编号。随后E行,每行给出一条边的两个端点的编号。在图的信息给出之后,给出了一个正整数N(20),是待检查的颜色分配方案的个数。随后N行,每行顺次给出V个顶点的颜色(第i个数字表示第i个顶点的颜色),数字间以空格分隔。题目保证给定的无向图是合法的(即不存在自回路和重边)。

输出格式:

对每种颜色分配方案,如果是图着色问题的一个解则输出Yes,否则输出No,每句占一行。

输入样例:

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

输出样例:

Yes
Yes
No
No
作者: 陈越
单位: 浙江大学
时间限制: 300ms
内存限制: 64MB
代码长度限制: 16KB

判断是否颜色相等
#include <bits/stdc++.h>
using namespace std;
int s[505][505],F[501];
int main()
{
    int v,e,k;
    cin>>v>>e>>k;
    for(int i=0,x,y; i<e; i++)
        cin>>x>>y,s[x][y]=s[y][x]=1;
    int T;
    cin>>T;
    map<int,int> M;
    for(int i=0; i<T; i++)
    {
        M.clear();
        int f=1,c=0;
        for(int j=1; j<=v; j++)
        {
            cin>>F[j];
            if(!M[F[j]])M[F[j]]++,c++;
        }
        if(c==k)
            for(int j=1; j<v&&f; j++)
            {
                for(int l=j+1; l<=v; l++)
                    if(F[j]==F[l]&&s[j][l])
                    {
                        f=0;
                        break;
                    }
            }
        else f=0;
        if(f)cout<<"Yes
";
        else cout<<"No
";
    }
    return 0;
}

7-12 部落(25 分)

在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。

输入格式:

输入在第一行给出一个正整数N(104​​),是已知小圈子的个数。随后N行,每行按下列格式给出一个小圈子里的人:

P[1P[2⋯ P[K]

其中K是小圈子里的人数,P[i](i=1,,K)是小圈子里每个人的编号。这里所有人的编号从1开始连续编号,最大编号不会超过104​​。

之后一行给出一个非负整数Q(104​​),是查询次数。随后Q行,每行给出一对被查询的人的编号。

输出格式:

首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出Y,否则输出N

输入样例:

4
3 10 1 2
2 3 4
4 1 5 7 8
3 9 6 4
2
10 5
3 7

输出样例:

10 2
Y
N
作者: 陈越
单位: 浙江大学
时间限制: 150ms
内存限制: 64MB
代码长度限制: 16KB

用并查集的基本操作就行了
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int fa[N],cnt[N];
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
    for(int i=0; i<N; i++)  fa[i]=i,cnt[i]=0;
    int n,q,k,a,b;
    cin>>n;
    for(int i=0; i<n; i++)
    {
        cin>>k>>a;
        cnt[a]=1;
        for(int j=1; j<k; j++)
        {
            cin>>b;
            cnt[b]=1;
            int x=find(a),y=find(b);
            if(x!=y)fa[x]=y;
        }
    }
    int s=0,num=0;
    for(int i=0; i<10004; i++)
    {
        if(cnt[i]==1)
        {
            s++;
            if(fa[i]==i)num++;
        }
    }
    printf("%d %d
",s,num);
    cin>>q;
    while(q--)
    {
        cin>>a>>b;
        if(find(a)==find(b))
            printf("Y
");
        else
            printf("N
");
    }
    return 0;
}

7-15 森森美图(30 分)

森森最近想让自己的朋友圈熠熠生辉,所以他决定自己写个美化照片的软件,并起名为森森美图。众所周知,在合照中美化自己的面部而不美化合照者的面部是让自己占据朋友圈高点的绝好方法,因此森森美图里当然得有这个功能。 这个功能的第一步是将自己的面部选中。森森首先计算出了一个图像中所有像素点与周围点的相似程度的分数,分数越低表示某个像素点越“像”一个轮廓边缘上的点。 森森认为,任意连续像素点的得分之和越低,表示它们组成的曲线和轮廓边缘的重合程度越高。为了选择出一个完整的面部,森森决定让用户选择面部上的两个像素点A和B,则连接这两个点的直线就将图像分为两部分,然后在这两部分中分别寻找一条从A到B且与轮廓重合程度最高的曲线,就可以拼出用户的面部了。 然而森森计算出来得分矩阵后,突然发现自己不知道怎么找到这两条曲线了,你能帮森森当上朋友圈的小王子吗?

为了解题方便,我们做出以下补充说明:

  • 图像的左上角是坐标原点(0,0),我们假设所有像素按矩阵格式排列,其坐标均为非负整数(即横轴向右为正,纵轴向下为正)。
  • 忽略正好位于连接A和B的直线(注意不是线段)上的像素点,即不认为这部分像素点在任何一个划分部分上,因此曲线也不能经过这部分像素点。
  • 曲线是八连通的(即任一像素点可与其周围的8个像素连通),但为了计算准确,某像素连接对角相邻的斜向像素时,得分是两个像素分数和的2​​倍。例如样例中,经过坐标为(3,1)和(4,2)的两个像素点的曲线,其得分应该是这两个像素点的分数和(2+2)乘以2​​,即约为5.66。

输入格式:

输入在第一行给出两个正整数N和M(5N,M100),表示像素得分矩阵的行数和列数。

接下来N行,每行M个不大于1000的非负整数,即为像素点的分值。

最后一行给出用户选择的起始和结束像素点的坐标(Xstart​​,Ystart​​)和(Xend​​,Yend​​)。4个整数用空格分隔。

输出格式:

在一行中输出划分图片后找到的轮廓曲线的得分和,保留小数点后两位。注意起点和终点的得分不要重复计算。

输入样例:

6 6
9 0 1 9 9 9
9 9 1 2 2 9
9 9 2 0 2 9
9 9 1 1 2 9
9 9 3 3 1 1
9 9 9 9 9 9
2 1 5 4

输出样例:

27.04
作者: 戴龙翱、朱建科
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m,vis[N][N],sx,sy,ex,ey;
double s[N][N],d[N][N],mi;
const double PI=sqrt(2)-1;
int dir[8][2]= {1,0,-1,0,0,1,0,-1,1,1,-1,-1,1,-1,-1,1};
struct T
{
    int x,y;
} a,b,p;
int chaji(T a,T b,T p)
{
    return (b.x-a.x)*(p.y-a.y)-(b.y-a.y)*(p.x-a.x);
}
void spfa()
{
    memset(vis,0,sizeof vis);
    memset(d,0x7f,sizeof d);
    a.x=sx,a.y=sy,b.x=ex,b.y=ey;
    d[sx][sy]=s[sx][sy];
    queue<T>Q;
    Q.push(a);
    while(!Q.empty())
    {
        int x=Q.front().x,y=Q.front().y;
        Q.pop();
        vis[x][y]=0;
        for(int i=0; i<8; i++)
        {
            double w=0;
            p.x=x+dir[i][0],p.y=y+dir[i][1];
            if(p.x<0||p.x>=n||p.y<0||p.y>=m) continue;
            if(chaji(a,b,p)>0||p.x==ex&&p.y==ey)
            {
                w=s[p.x][p.y];
                if(i>3) w=w+(s[x][y]+s[p.x][p.y])*PI;
                if(d[p.x][p.y]>d[x][y]+w)
                {
                    d[p.x][p.y]=d[x][y]+w;
                    if(!vis[p.x][p.y])Q.push(p),vis[p.x][p.y]=1;
                }
            }
        }
    }
    mi+=d[ex][ey];
}
int main()
{
    cin>>n>>m;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            cin>>s[i][j];
    cin>>sy>>sx>>ey>>ex;
    mi=-s[ex][ey]-s[sx][sy];
    spfa();
    swap(ex,sx),swap(ey,sy);
    spfa();
    printf("%.2f",mi);
    return 0;
}

原文地址:https://www.cnblogs.com/BobHuang/p/8361990.html