ICPC North Central NA Contest 2017 计蒜客重现 解(补)题报告

前言

不知不觉又到了周六训练赛,时间过得真快。手机前段时间被我不小心摔坏了,没手机玩的第十天, 无奈只能学习了 ,想它。题目还是要做的,题解还是要写的,CF还是得抽时间打的,所以说手机坏了貌似是好事。突然发现今天是2.29,还是想纪念一下的,如今大二,四年后的今天再看到这篇文章时,我会在哪?会是什么身份?会变成什么样子?一切都是未知数,所以,珍惜当下,好好学习,努力做题,认真比赛,才是我应该做的!

A - Stoichiometry(高斯消元)

https://blog.csdn.net/qq_44691917/article/details/104601827

C - Urban Design(直线和线段相交点个数)

题目链接

A new town is being planned, and the designers have some very specific ideas about how things should be laid out. First, they lay out the streets. Each street is perfectly straight and passes completely from one end of the town to the other. These streets divide the town into regions, and each region is to be designated either"residential"or"commercial".The town planners require that any two regions directly across the street from one another must have different designations. On this one particular day, all of the streets have been planned, but none of the regions have been designated. One town planner wishes to purchase two properties, and it is important to him that the properties eventually have different designations. For this problem, the streets can be modeled by lines in the plane that extend forever in both directions and have no width, and properties may be modeled by points. Given the lines and two points, can you decide whether or not they must get different designations,“commercial"or"residential”?

Input
Input begins with an integer S on a single line, giving the number of streets (1≤S≤10000). The next S lines of input each contain four integers . The unique line through these two points gives one of the streets. Each coordinate is in the range [0,10000], and no two lines will be identical. That is, the town will have S distinct streets. The next line contains an integer T, the number of pairs of properties to test (1≤T≤1000). This is followed by T lines of input, each containing four integers … representing two distinct points (x3,y3) and (x4,y4), where each point lies within one of the two properties to test. None of these points will lie on any of the streets, nor will both points lie within the same property. Again, each coordinate is in the range [0,10000].

Output
For each of the TT pairs of properties to be tested, output either"same"if the properties are guaranteed to receive the same designation or"different"if they are guaranteed to receive different designations.

1.题目大意:给出一些直线,把这些直线的边当做街道来看,如果有两点在直线两侧,那么他们属性不同,否则属性相同。给出若干直线和若干点,判断两点的关系

2.容易得出,当两个点连成的线段经过奇数条直线时,属性相同,否则属性不同。学过计算几何基础的都知道,用向量表示线段和直线,那么利用向量的叉积进行一次跨立实验即可。从直线上的任意两点向线段的其中一个端点作向量,如下。那么由于c1c2 肯定在 a.v 的两端,那么 a.vc1c2 分别求向量积,根据向量积的定义来看,结果肯定是结果符号相反——方向相反,注意右手定则(当右手的四指从 a 以不超过180度的转角转向 b 时,竖起的大拇指指向是c的方向)
在这里插入图片描述

#include <iostream>
#include <math.h>
using namespace std;

#define Point Vector
const double PI=acos(-1.0);
const double eps=1e-8;

inline int dcmp(double d){   //浮点数和0比较
	if(fabs(d)<eps) return 0;
	return d>0?1:-1;
}

struct Point{
    double x,y;
    Point(double a=0,double b=0):x(a),y(b){}

    Vector operator + (Vector B){
        return Vector(x+B.x,y+B.y);
    }

    Vector operator - (Point B){
        return Vector(x-B.x,y-B.y);
    }

    Vector operator * (double d){  //数乘
        return Vector(x*d,y*d);
    }

    double operator * (Vector B){  //数量积
        return x*B.x+y*B.y;
    }

    Vector operator / (double d){
        return Vector(x/d,y/d);
    }

    double operator ^ (Vector B){  //叉乘
        return x*B.y-y*B.x;
    }

    bool operator < (const Point &b) const {
        if(x==b.x) return y<b.y;
        return x<b.x;
    }

    bool operator == (const Point& b) const {
        if(dcmp(x-b.x)==0 && dcmp(y-b.y)==0)
            return true;
        return false;
    }
};


struct Line{
	Point p,q;
    Vector v;
    Line(){}
	Line(Point a,Point b){		//构造函数
    	p=a,q=b,v=b-a;
    }
};


bool isLineInter(Line a,Line b){		//是否相交判断函数
    double c1=a.v^b.p-a.p,c2=a.v^b.q-a.p;
    return dcmp(c1)*dcmp(c2)<0;
}

const int maxn=1e4+10;
Line a[maxn];
int n,m;

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    Point p,q;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>p.x>>p.y>>q.x>>q.y;
        a[i]=Line(p,q);
    }
    cin>>m;
    while(m--){
        int tot=0;
        cin>>p.x>>p.y>>q.x>>q.y;
        Line l(p,q);
        for(int i=1;i<=n;i++){
            if(isLineInter(a[i],l)) tot++;
        }
        if(tot&1) cout<<"different\n";
        else cout<<"same\n";
    }
    return 0;
}

E - Is-A? Has-A? Who Knowz-A? (Floyd求传递闭包)

https://blog.csdn.net/qq_44691917/article/details/104583850

F - Atlantis(贪心)

题目链接

You may have heard of the lost city of Atlantis. As legend goes, Atlantis was a city of great wealth and power. Then the Gods became displeased with Atlantis and sank it into the ocean. What you may not have heard is the story of Demetrios, the only person in Atlantis with a ship at the time the city was pushed underwater.Demetrios was an incredibly selfish man and thought of only one thing when he noticed the water level in the city rising: collecting the wealth of accessible gold in Atlantis and transporting it into his ship before the treasure was lost forever. Luckily for Demetrios, when the water level began rising, the many guards keeping safe the gold stores abandoned their posts to seek safety.
Demetrios knows the location of every gold store in Atlantis and how long it will take him to get to a particular store and back to his ship. He also knows the altitude at which each gold store resides, which determines when the store will be swallowed by the rising sea. The trouble is that he’s not sure he’ll have time to make it to every store before they become submerged. He now wonders the maximum number of stores he can visit prior to their respective submersion, if he picks his schedule optimally.During the 2017 NCNA Regional, the following clarification was posted: “Important: The gold store must remain above water during the ENTIRE trip to and from the store.”

Input
The first line of input will contain the integer nn, (1≤n≤200000)(1≤n≤200000), the number of gold stores in Atlantis. The next nn lines will contain two integers on each line, the round-trip time in seconds it will take Demetrios to visit store ii and return to his ship with the gold, and the feet above sea level of store ii, respectively.

Output
Output the maximum number of gold stores Demetrios can visit such that each is visited prior to it becoming submerged. Assume sea level rises by one foot every second, starts at height 00, and begins rising immediately.

1.题目大意:现在有n个商店,给出访问商店所需要的的时间和商店的海拔。现在海水从高度为0的地方开始上升,单位时间内上升单位高度,那么求这个人在海水淹没所有商店之前最多能访问多少商店

2.题目即不难读也不难想,但是不知道为什么没几个人做,可能是看到80多的提交,20多的通过然后就直接Pass(俺也一样)。这种贪心题一般是几个限制条件,让我们去求一个最值。本题就是两个限制条件,一个高度,另外一个是时间,一般都是固定一个条件去考虑另外一个条件。那么我们假设先按时间贪心,那么什么时候不能选呢?当前时间累积的和(当前高度)大于了后面的高度,但是如果有一个数据,高度很大(前面的高度都远小于它),时间和其他数据的时间相差不多,那么如果我们贪心选它了,就会导致当前高度累积了一定的值,这个高度对它影响不大,但是可能导致高度较小的无法选择,例如:
t h
3 6
3 4
5 17
6 10
按时间贪心的话只能选择三个,但是实际上如果先选择时间消耗为6、高度为10的商店,那么一共能选4个

3.既然时间不行,那我们试试按高度贪心,如果把当前高度按从小到大排列。每判断一个高度能否选择,还是看如果当前高度加上它的时间能否小于等于它的高度。那么这样问题就变成了,如何让当前总高度最小,假设我们选择了若干商店,后面的商店需要的时间更少而且高度较高,那么显然我们就把当前集合中花费时间更多的商店去掉,进而添加进去时间更少的商店,这样当前高度就会更小,而当前选择的商店总量并没有变,意味着后面有更大的空间

4.综上,我们先对高度从小到大排序,高度相同的按时间从小到大排序。接着使用最小堆的优先队列维护当前消耗的最大时间,可以保证的是,队列中的总高度(当前海拔)一定小于后面所选商店的高度。如果可选择该商店就选,否则就看能否替换队列中消耗最大的

代码:

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
struct node{
    ll t,h;
    bool operator < (const node &s) const {  //自定义比较符用于构造时间最小堆的优先队列
        if(t==s.t) return h<s.h;    //当时间相同时高度大小其实无所谓,这里大于小于都正确
        return t<s.t;
    }

}a[maxn];
int n;

bool cmp(node &s1,node &s2){  //对高度排序
    if(s1.h==s2.h) return s1.t<s2.t;
    return s1.h<s2.h;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i].t>>a[i].h;
    sort(a,a+n,cmp);
    //for(int i=0;i<n;i++) cout<<a[i].t<<"  "<<a[i].h<<endl;
    ll high=0;
    priority_queue<node> q;
    for(int i=0;i<n;i++){
        if(high+a[i].t<=a[i].h){
            high+=a[i].t;
            q.push(a[i]);
        }else  if(!q.empty()){ //有可能队列为空,不然RE
            node u=q.top();
            if(a[i].t<u.t){
                q.pop();
                q.push(a[i]);
                high-=u.t;
                high+=a[i].t;
            }
        }
    }
    cout<<q.size()<<endl;
    return 0;
}

G - Sheba’s Amoebas (DFS求连通块)

题目链接

After a successful Kickstarter campaign, Sheba Arriba has raised enough money for her mail-order biology supply company. “Sheba’s Amoebas” can ship Petri dishes already populated with a colony of those tiny one-celled organisms. However, Sheba needs to be able to verify the number of amoebas her company sends out. For each dish she has a black-and-white image that has been pre-processed to show each amoeba as a simple closed loop of black pixels. (A loop is a minimal set of black pixels in which each pixel is adjacent to exactly two other pixels in the set; adjacent means sharing an edge or corner of a pixel.) All black pixels in the image belong to some loop.Sheba would like you to write a program that counts the closed loops in a rectangular array of black and white pixels. No two closed loops in the image touch or overlap. One particularly nasty species of cannibalistic amoeba is known to surround and engulf its neighbors; consequently there may be amoebas within amoebas. For instance, each of the images in figure below contains four amoebas.
在这里插入图片描述
Input
The first line of input contains two integers mm and nn, (1≤m,n≤100)(1≤m,n≤100). This is followed by mm lines, each containing nn characters. A ‘#’ denotes a black pixel, a ‘.’ denotes a white pixel. For every black pixel, exactly two of its eight neighbors are also black.

Output
Display a single integer representing the number of loops in the input.

1.题目大意:给出一个m×n的方格棋盘,定义在某#格周围的八个方格内如果仍有#,那么他们同属一个连通块。求连通块的个数

2.DFS/BFS,棋盘搜索入门级别的题。如果有紫书的话会发现DFS的第一道例题和这个几乎一样

代码:

#include <iostream>

using namespace std;
int m,n;
char a[105][105];
bool vis[105][105];

void dfs(int r,int c){
    if(r<1 || c<1 || r>m || c>n) return;
    if(vis[r][c] || a[r][c]=='.') return;
    vis[r][c]=1;
    for(int i=-1;i<=1;i++)
    for(int j=-1;j<=1;j++){
        if(i!=0 || j!=0) dfs(r+i,c+j);
    }
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>m>>n;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            cin>>a[i][j];
    int ans=0;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            if(vis[i][j]==0 && a[i][j]=='#'){
                dfs(i,j);
                ans++;
            }
        }
    cout<<ans<<endl;
    return 0;
}

H - Zebras and Ocelots(找规律)

题目链接

The famous City Central Zoo houses just two creatures, Zebras and Ocelots. These magical creatures spend their time in a single, tall column, each standing atop the back of the creature below it. Whenever the zoo bell rings, the ocelot lowest in the pile turns into a zebra, and all zebras (if there are any) below that creature simultaneously turn into ocelots. If there is no ocelot in the pile when the bell tolls, then nothing happens. The zookeeper has been watching this interesting process for years. Today, suddenly, he begins to wonder how much longer this can go on. That is, given a pile of zebras and/or ocelots, how many times may the bell toll before there are no more ocelots?

Input
Input consists of a number NN in the range 11 to 6060, followed by NN lines, each of which is a single character, either ZZ (for zebra) or OO (for ocelot). These give the order of the creatures from top (first) to bottom (last).

Output
Output should be a single integer giving the number of times the bell must toll in order for there to be no more ocelots.

1.题目大意:给出一个Z和O的序列,每次变化序列最后的O变成Z,后面的Z变成O,问什么时候该序列没有O全是Z

2.显然我们可以把题意转换成1和0的序列,刚开始我一看数组大小60,以为可以直接模拟做,但是发现超时。看着大家都做出来了,我慌了。大脑飞速思索,如何优化把每一段0变成1的时间,嗯!树状数组,那么怎么得到最后一个0呢,差分!兴致勃勃的我立刻敲啊敲,结果交了一发又超时。┭┮﹏┭┮,已经过了二百多了。这时队长提醒我直接找规律就行了,我简单推到倒数第三位时豁然开朗,原来倒数第几位的0把它变为1的次数是2n-1,那么直接快速幂就可以了!

3.以后想出办法多去试着分析一下时间复杂度,不要导致无意义的TLE

代码:

#include <iostream>
using namespace std;
typedef long long ll;
int n,a[105];
char c;

ll quick_pow(ll x,ll n){
    ll ans=1;
    while(n){
        if(n&1) ans*=x;
        x*=x;
        n>>=1;
    }
    return ans;
}

int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=n;i>=1;i--){
        cin>>c;
        if(c=='O'){
            a[i]=0;
        }else{
            a[i]=1;
        }
    }
    ll ans=0;
    for(int i=1;i<=n;i++){
        if(!a[i])
        ans+=quick_pow(2,i-1);
    }
    cout<<ans<<endl;
    return 0;
}

I - Racing Around the Alphabet(贪心)

题目链接

Soccer coach Joe has invented a new fitness game that he thinks will help his players’ agility and fitness. The center circle of the soccer pitch has a diameter of 6060 feet. He marks 2828 evenly-spaced points on the circumference of the circle. On the first point (arbitrarily chosen) he places a pile of tokens all inscribed with the letter ‘A’. On the second point he places a pile of ‘B’ tokens, and so on, covering 2626 of the 2828 points. He places a pile of blanks (or space-characters) on the 27^{th}27 th point and a pile of disks imprinted with single quotation marks, on the last point.He gives each player a slip of paper containing an aphorism, such as “WINNING ISN’T EVERYTHING IT’S THE ONLY THING” Notice that the only punctuation marks allowed are spaces and single quotation marks. Each player gets a different aphorism.A player starts outside the circle next to the first letter of his aphorism and, on Joe’s “GO” signal, the player picks up his first disk and then takes off running around the outside of the circle, picking up the remaining disks in the order that the corresponding letters appear in the aphorism. A smart player will take the shortest route, possibly changing direction, between consecutive letters of his aphorism.One of the assistant coaches makes a note of the player’s time. Although Joe initially envisioned all of his 2020 players running around the circle simultaneously, this idea was scrapped due to contusions and fractures.Joe wants you to write a program to estimate the expected time for one smart player to complete each of the aphorisms. Assume for simplicity that the player runs at 1515 feet per second between stops for pickups and that each pickup takes 11 second.
在这里插入图片描述
Input
The input begins with a number NN, (1≤N≤20)(1≤N≤20) giving the number of aphorisms to follow. NN lines follow, each one containing an aphorism made up of upper-case letters, single quotation marks, and spaces. Each aphorism will be between 88 and 120120 characters inclusive.

Output
For each aphorism give the time for a smart player to complete the task. Your answer should be accurate to within 10^{−6}10 −6 seconds.

1.题目大意:给出一个圆环,分成28份,每一份按顺序对应一个字母或另外两个字符。现在给出一句话,我们从这句话的第一个字母出发,走到最后一个字母所用的时间

2.先把28个字符用map对应圆上0-27的位置。显然,当我们每从一个字母到另外一个字母时,每次都有两种路可以走,因为字母必须按出现顺序拾取,显然直接算出两条路选择最短路即可。一条是两个字母直接距离(绝对值),另一种是通过Z和A中转的距离

代码:

#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string>
#include <map>
using namespace std;
const double D=60*M_PI/28;  //使用math头文件下的PI
int t;
string s;
map<char,int> mp;

void init(){
    for(int i=0;i<26;i++)
        mp[i+'A']=i;
    mp[' ']=26;
    mp['\'']=27;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>t;
    cin.ignore();  //吸收回车
    init();
    while(t--){
        getline(cin,s);
        int p=mp[s[0]];
        double ans=0.0;
        for(int i=1;i<s.size();i++){
            int res;
            if(p>mp[s[i]]) res=mp[s[i]]+28-p;
            else res=p+28-mp[s[i]];
            int tmp=abs(mp[s[i]]-p);
            if(tmp<res) ans+=(double)tmp;
            else ans+=(double)res;
            p=mp[s[i]];
        }
        ans=ans*D/15;
        ans+=s.size();
        printf("%.10lf\n",ans);
    }
    return 0;
}

J - Lost Map (打印最小生成树)

题目链接

Somewhere in a mountainous region of the world is a collection of nn villages. Connecting these villages to one another is a series of roads, always running directly between two villages and allowing travel in both directions. Due to the treacherous landscape, building these roads is expensive, so the minimum number of roads have been constructed such that each village can reach every other village via a sequence of roads.

Trade between these villages is very important, since not every village has access to the same supply of natural resources. Many villages produce the same resource, however, so it is useful for villages to know their relative distance to other villages so that they can choose trading partners to minimize overall trading costs. Note that the distance between two villages aa and bb is the sum of the lengths of the individual roads on the shortest path that connects aa and bb.

A project has been underway to compute the distance between every pair of villages. This information has been incorporated in a table, along with a map that shows the layout of villages and roads that run between them. You have been assigned the task of distributing the table and map to all the villages for the betterment of the regional economy.Unfortunately, not long after you were given this task, a gust of wind blew the map right out of your hand and into the countryside. Despite your best efforts of searching for it, you have been unable to locate it. You know that you could visit all the villages to reconstruct the map and THEN start distributing the map and table, but this will take twice as long as the original task and the villages will be very displeased with you. You wonder, maybe it’s possible to reconstruct the map given only the table?

Input
The first line of input will contain the integer nn (2≤n≤2500)(2≤n≤2500), the number of villages in this region. The next nn lines will contain nn integers each. The j th integer of the i th line is the distance from village ii to village jj. All distances are greater than zero unless i=ji=j, less than 10^710 7 , and it is guaranteed that the distance from village ii to village jj is the same as the distance from village jj to village ii.

Output
For each test case, output n−1n−1 lines with two integers uu and vv on each line, indicating that there is a road connecting villages uu and vv in this region. Assume the villages are numbered from 11 to nn. Any solution that outputs the original set of roads will be accepted.

1.题目大意:给出一个含有n个节点且任意两节点都有一条路的无向图,求最小生成树并分开打印路径

2.Kruskal直接写,但是注意Kruskal是按边来写的,一般基于邻接表。因此我们可以先用邻接矩阵存储,接着肯定有n*(n-1)/2条边,那么我们取出邻接矩阵的一半来保存边即可。虽然Prim算法比较适合稠密图,但是我下去之后看了Prim算法,是以点为基础操作的,每次都是单个点不断加入到最小生成树,也许有记录每条边的方法,但是我不知道怎么写,如果Prim能解决该题请大佬务必留言,不胜感激

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=4e6+10;
struct node{
    int from,to,v;
}edge[N];

int G[2505][2505];
int father[N];
int n,m; //节点数和边数

bool cmp(node &a,node &b){
    return a.v<b.v;
}

int Find(int x){
    return father[x]==x?x:father[x]=Find(father[x]);
}

void Kruskal(){
    for(int i=1;i<=n;i++) father[i]=i;
    sort(edge+1,edge+1+m,cmp);
    int res=0;
    for(int i=1;i<=m;i++){
        if(res==n-1) break;
        int fx=Find(edge[i].from);
        int fy=Find(edge[i].to);
        if(fx!=fy){
           father[fy]=fx;
           printf("%d %d\n",edge[i].from,edge[i].to);
           res++;
        }
    }
}

int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    m=n*(n-1)/2;
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>G[i][j];
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++){
            edge[++cnt].from=i;
            edge[cnt].to=j;
            edge[cnt].v=G[i][j];
        }
    Kruskal();
}

发布了128 篇原创文章 · 获赞 7 · 访问量 5266

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/104583481