CAIP 2021初赛

发布时间 2023-07-10 15:30:00作者: 次林梦叶

 

《7-1 懂的都懂》

 这道题其他没什么,就是暴力

但是注意上面,我们算平均值的时候要用double

如果对double不放心(因为double其实有时候并不准确)

可以写成如下样子:

  

    

 

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int N=52;
int n,m,arr[N];
map<int,bool>mp;
int main()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)
        cin>>arr[i];
    for (int i=1;i<=n;i++)
        for (int j=i+1;j<=n;j++)
            for (int k=j+1;k<=n;k++)
                for (int l=k+1;l<=n;l++)
                {
                    int ans=(arr[i]+arr[j]+arr[k]+arr[l]); 
                    mp[ans]=true;
                }
    while (m--)
    {
        cin>>n;
        int num;
        bool flag=true;
        for (int i=1;i<=n;i++)
        {
            cin>>num;
            if (mp[num*4]!=true)
                flag=false;
        }
        if (flag)
            cout<<"Yes";
        else cout<<"No";
        if (m)
            cout<<endl;
    }
    return 0;
}

 

 

 

《7-2 芬兰木棋》

这道题要考虑的细节比较多:

  1.首先要考虑当斜率计算不出来(即x==0)的情况

  2.还有考虑在每个斜率上点的排序

   不同地方点的排序方法不一样

   比如:

    在X正轴上的点要按照x从小到大排序

    但是在X负轴上的点要按照x从大到小排序

    在Y正轴上x是一样的,这个时候要按照y从小到大排序

    在Y负轴上......等

  

 

 同样,为了避免double可能不太精确的问题,这里用

  记录 y/x 中 记录 y与x的方式记录斜率

  同时将坐标轴分成4个象限

  对于每个象限上的每个斜率上的点用vector装好,排序

  

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#include<set>
using namespace std; 
typedef pair<int,int> PII; 
struct node{
    int x,y,p;
};
bool rule1(struct node a,struct node b){
    if (a.x==b.x)
        return a.y>b.y;
    else return a.x<b.x;
}
bool rule2(struct node a,struct node b){
    if (a.x==b.x)
        return a.y<b.y;
    else return a.x>b.x;
}

map<PII,vector<node>>mp[5];
set<PII>st[5];
int n,ans=0,cnt=0;
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
void input()
{
    cin>>n;
    for (int i=1;i<=n;i++)
    {
        int x,y,p;
        cin>>x>>y>>p;
        ans+=p;
        int k=gcd(x,y);
        if (y>=0 && x>0)
            mp[1][{y/k,x/k}].push_back({x,y,p}),
            st[1].insert({y/k,x/k});
        else if (y>0 && x<=0)
            mp[2][{y/k,x/k}].push_back({x,y,p}),
            st[2].insert({y/k,x/k});
        else if (y<=0 && x<0)
            mp[3][{y/k,x/k}].push_back({x,y,p}),
            st[3].insert({y/k,x/k});
        else if (y<0 && x>=0)
            mp[4][{y/k,x/k}].push_back({x,y,p}),
            st[4].insert({y/k,x/k});
    }
}
int main()
{
    input();
    for (int i=1;i<=4;i++)
    {
        //枚举出第i象限的k来
        for (auto j=st[i].begin();j!=st[i].end();j++)
        {
            PII k=*j;
            //通过k枚举这个象限上,斜率k上的点
            if (i==1 || i==4) 
                sort(mp[i][k].begin(),mp[i][k].end(),rule1);
            else
                sort(mp[i][k].begin(),mp[i][k].end(),rule2);
            int buf=0;
            for (int l=0;l<mp[i][k].size();l++)
            {
                node t=mp[i][k][l];
                if (t.p==1)
                    buf++;
                else 
                {
                    if (buf!=0)
                        cnt++,buf=0;
                    cnt++;
                }    
            }
            if (buf!=0)
                cnt++,buf=0;
        }
    }
    cout<<ans<<" "<<cnt<<endl;
    return 0;    
} 

 

 

《7-3 打怪升级》

 我总感觉这句话没说清楚

总之这个点的求解方式是:

  先Floyd,求出每个点相互之间的最小距离

  然后枚举每个点i,找出其中到某个点j的最大距离maxwi

  然后找出这些maxwi中最小的那个,其所对应的点i就是我们要找的点

 

然后这题的关键就是用一次Floyd解决:

  1.记录最短路路径问题

  2.解决双决策标准问题

void Floyd()
{
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
            ansW[i][j]=gw[i][j];
            ansV[i][j]=gv[i][j];
        }
    for (int k=1;k<=n;k++)
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
            {
                if (ansW[i][j]>ansW[i][k]+ansW[k][j])
                {
                    ansW[i][j]=ansW[i][k]+ansW[k][j];
                    ansV[i][j]=ansV[i][k]+ansV[k][j];
                    path[i][j]=k;
                }
                else if (ansW[i][j]==ansW[i][k]+ansW[k][j])
                {
                    if (ansV[i][j]<ansV[i][k]+ansV[k][j])
                    {
                        ansV[i][j]=ansV[i][k]+ansV[k][j];
                        path[i][j]=k;
                    }
                }
            }
}

 

 

path[i][j]:表示点i到点j的最短路中,需要经过点path[i][j]

初始化 path初始全0

当我记录了path[N][N]该如何求出最短路呢?

  递归!

void printRoad(int fr,int to)
{
    if (fr==to)
        return ;
    if (path[fr][to]==0)
    {
        cout<<fr<<"->";
        return ;
    }
    printRoad(fr,path[fr][to]);
    printRoad(path[fr][to],to);
    return ; 
}

最后在函数返回处的后面输出

cout<<to<<endl;

 

 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1002,INF=0x3f3f3f3f;
int gw[N][N],gv[N][N],n,m;
int ansW[N][N],ansV[N][N],path[N][N];
void Floyd()
{
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
            ansW[i][j]=gw[i][j];
            ansV[i][j]=gv[i][j];
        }
    for (int k=1;k<=n;k++)
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
            {
                if (ansW[i][j]>ansW[i][k]+ansW[k][j])
                {
                    ansW[i][j]=ansW[i][k]+ansW[k][j];
                    ansV[i][j]=ansV[i][k]+ansV[k][j];
                    path[i][j]=k;
                }
                else if (ansW[i][j]==ansW[i][k]+ansW[k][j])
                {
                    if (ansV[i][j]<ansV[i][k]+ansV[k][j])
                    {
                        ansV[i][j]=ansV[i][k]+ansV[k][j];
                        path[i][j]=k;
                    }
                }
            }
}
int sta;
void find()
{
    int minw=INF,maxw=0;
    for (int i=1;i<=n;i++)
    {    
        maxw=0;
        for (int j=1;j<=n;j++)
        {
            if (ansW[i][j]<INF)
                maxw=max(maxw,ansW[i][j]);
        }
        if (minw>maxw)
            minw=maxw,sta=i;
    }
}
void printRoad(int fr,int to)
{
    if (fr==to)
        return ;
    if (path[fr][to]==0)
    {
        cout<<fr<<"->";
        return ;
    }
    printRoad(fr,path[fr][to]);
    printRoad(path[fr][to],to);
    return ; 
}
int main()
{
    memset(gw,0x3f,sizeof(gw));
    memset(gv,-0x3f,sizeof(gv));
    cin>>n>>m;
    for (int i=1;i<=n;i++)
        gw[i][i]=gv[i][i]=0;
    for (int i=1;i<=m;i++)
    {
        int b1,b2,w,v;
        cin>>b1>>b2>>w>>v;
        gw[b1][b2]=gw[b2][b1]=w;
        gv[b1][b2]=gv[b2][b1]=v;
    }
    Floyd();
    find();
    cout<<sta<<endl;
    int t;
    cin>>t;
    while (t--)
    {
        int tar;
        cin>>tar;
        printRoad(sta,tar);
        cout<<tar<<endl;
        cout<<ansW[sta][tar]<<" "<<ansV[sta][tar]<<endl;        
    }
    return 0;
} 

 

 

 

《7-4 疫情防控》

 问能否从某个地方到某个地方

其实就是在问这两个点的连通性咋样

对应连通性的问题当然用并查集

 

这里可以用加点的方式(而不是按照题目惯性思维来删点),来解决复杂度过高的问题

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
typedef pair<int,int> PII;
const int N=5*1e4;
vector<int>sides[N];
vector<PII>cm[N];
int n,m,d,city[N],f[N];
bool die[N];
int find(int x)
{
    if (f[x]!=x)
        f[x]=find(f[x]);
    return f[x];
}
int main()
{
    cin>>n>>m>>d;
    for (int i=1;i<=m;i++)
    {
        int a,b;
        cin>>a>>b;
        sides[a].push_back(b),sides[b].push_back(a);
    }
    for (int i=1;i<=d;i++)
    {
        int c,q;
        cin>>c>>q;
        city[i]=c,die[c]=true;
        while (q--)
        {
            int x,y;
            cin>>x>>y;
            cm[i].push_back({x,y});
        }
    }
    for (int i=1;i<=n;i++)
        f[i]=i;
    for (int i=1;i<=n;i++)
    {
        if (die[i])
            continue;
        for (int j=0;j<sides[i].size();j++)
        {
            int ch=sides[i][j];
            if (die[ch])
                continue;
            int fa=find(i),fb=find(ch);
            if (fa!=fb)
                f[fb]=fa;     
        }
    }
    stack<int>ans;
    for (int i=d;i>=1;i--)
    {
        int dieC=city[i];
        int cnt=0;
        for (int j=0;j<cm[i].size();j++)
        {
            int x=cm[i][j].first,y=cm[i][j].second;
            int fx=find(x),fy=find(y);
            if (fx!=fy)
                cnt++;
        }
        ans.push(cnt);
        die[dieC]=false;
        for (int j=0;j<sides[dieC].size();j++)
        {
            int ch=sides[dieC][j];
            if (die[ch])
                continue;
            int fa=find(dieC),fb=find(ch);
            if (fa!=fb)
                f[fb]=fa;
        }
    }
    while (ans.size())
    {
        cout<<ans.top()<<endl;
        ans.pop();
    }
    return 0;
}