ZUST 集训队19届菜鸡互啄-2 题解

A-Hierarchy
最小生成树,在建树时要判断一下后面那个点有没有被管理过,加一个VIS记录即可

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
struct node
{
    int x,y,z;
}e[maxn];
int fa[maxn],a[maxn];
bool vis[maxn];
bool cnm(node a,node b)
{
    return a.z<b.z;
}
int find(int x)
{
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}
int main()
{
    int n,m;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>e[i].x>>e[i].y>>e[i].z;
    }
    sort(e+1,e+m+1,cnm);
    int cnt=0,ans=0;
    for(int i=0;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int fx=find(e[i].x);
        int fy=find(e[i].y);
        if(fx!=fy &&!vis[e[i].y])
        {
            vis[e[i].y]=1;
            fa[fx]=fy;
            ans+=e[i].z;
            cnt++;
        }
        if(cnt==n-1)break;
    }
    if(cnt==n-1)cout<<ans<<endl;
    else cout<<-1<<endl;
    return 0;
}

B-变形课
相传搜索可以做 但是我是用最短路写的。输入一个字符串,首字母和尾字母建边,长度为1,跑一遍floyed,看一下B-M有没有路径就可

#include<bits/stdc++.h>
using namespace std;
const int inf=100;
int mp[40][40];
void intit()
{
    for(int i=1;i<=26;i++)
    {
        for(int j=1;j<=26;j++)
        {
            mp[i][j]=inf;
            if(i==j)mp[i][j]=0;
        }
    }
}
int main()
{
    char s[1000];
    intit();
    while(scanf("%s",s)!=EOF)
    {
        if(s[0]=='0')
        {
            for(int k=1;k<=26;k++)
            {
                for(int i=1;i<=26;i++)
                {
                    for(int j=1;j<=26;j++)mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
                }
            }
            if(mp[2]['m'-'a'+1]<inf)puts("Yes.");
            else puts("No.");
            intit();
        }
        else
        {
            int l=strlen(s);
            mp[s[0]-'a'+1][s[l-1]-'a'+1]=1;
        }
        
    }
    return 0;
}


C-Average Numbers
签到题 没什么好说的

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int a[maxn];
int main()
{
    int n;
    cin>>n;
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        tot+=a[i];
    }
    vector<int> e;
    for(int i=1;i<=n;i++)
    {
        if((tot-a[i])%(n-1)==0 && (tot-a[i])/(n-1)==a[i])e.push_back(i);
    }
    cout<<e.size()<<endl;
    for(int i=0;i<e.size();i++)cout<<e[i]<<" ";
    return 0;
}

D-Exposition
题目大意:求区间最大值和最小值小于等于K的最长子区间 输出区间起点和终点,如果有多个区间,全部输出
线段树存区间最大和最小值,然后以每一个点为起点,最后一点为终点,二分区间

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
struct node
{
    int l,r,mi,ma;
}tree[maxn<<2];
int ansx[maxn],ansy[maxn];
void build(int root,int l,int r)
{
    tree[root].l=l;
    tree[root].r=r;
    if(l==r)
    {
        scanf("%d",&tree[root].mi);
        tree[root].ma=tree[root].mi; 
        return;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    tree[root].ma=max(tree[root<<1].ma,tree[root<<1|1].ma);
    tree[root].mi=min(tree[root<<1].mi,tree[root<<1|1].mi);
}
int mi,ma;
void que(int root,int l,int r)
{
    if(l<=tree[root].l && tree[root].r<=r)
    {
        mi=min(mi,tree[root].mi);
        ma=max(ma,tree[root].ma);
        return;
    }
    int mid=(tree[root].l+tree[root].r)>>1;
    if(r<=mid)que(root<<1,l,r);
    else if(l>mid)que(root<<1|1,l,r);
    else que(root<<1,l,mid),que(root<<1|1,mid+1,r);
}
int main()
{
    int n,k,len=0,cnt=0,a1=1;
    scanf("%d%d",&n,&k);
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        int l=i,r=n;
        len=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            mi=inf;
            ma=-inf;
            que(1,i,mid);
            if(ma-mi >k)r=mid-1;
            else
            {
                l=mid+1;
                len=max(len,mid);
            }
        }
        mi=inf;
        ma=-inf;
        que(1,i,len);
        if(ma-mi<=k)
        {
            if(len-i+1 > a1)
            {
                a1=len-i+1;
                cnt=1;
                ansx[cnt]=i;
                ansy[cnt]=len;
            }
            else if(len-i+1 == a1)
            {
                cnt++;
                ansx[cnt]=i;
                ansy[cnt]=len;
            }
        }
    }
    printf("%d %d\n",a1,cnt);
    for(int i=1;i<=cnt;i++)printf("%d %d\n",ansx[i],ansy[i]);
    return 0;
}

E-String Problem
最短路,每两个字母的转化看成一条有向边。先跑一边floyed求出每个字母转化成每个字母的最小费用,然后再开始遍历,如果有不能转化的字母输出-1

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int mp[30][30];
int main()
{
    string s1,s2,s;
    cin>>s1>>s2;
    s="";
    int n,w;
    char a,b;
    cin>>n;
    for(int i=1;i<=26;i++)
    {
        for(int j=1;j<=26;j++)
        {
            mp[i][j]=inf;
            if(i==j)mp[i][j]=0;
        }
    }
    for(int i=1;i<=n;i++)
    {
        cin>>a>>b>>w;
        mp[a-'a'+1][b-'a'+1]=min(mp[a-'a'+1][b-'a'+1],w);
    }
    if(s1.size()!=s2.size())
    {
        cout<<-1<<endl;
        return 0;
    }
    for(int k=1;k<=26;k++)
    {
        for(int i=1;i<=26;i++)
        {
            for(int j=1;j<=26;j++)
            {
                mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
            }
        }
    }
    int ans=0;
    for(int i=0;i<s1.size();i++)
    {
        if(s1[i]!=s2[i])
        {
            int tmp=1;
            for(int j=1;j<=26;j++)
            {
                if((mp[s1[i]-'a'+1][j]+mp[s2[i]-'a'+1][j])<(mp[s1[i]-'a'+1][tmp]+mp[s2[i]-'a'+1][tmp]))tmp=j;
            }
            ans+=mp[s1[i]-'a'+1][tmp]+mp[s2[i]-'a'+1][tmp];
            s+=('a'+tmp-1);
        }
        else
        {
            s+=s1[i];
        }
        if(ans>inf)break;
    }
    if(ans>inf)
    {
        cout<<-1<<endl;
        return 0;
    }
    cout<<ans<<endl<<s<<endl;
    return 0;
}

F-Knights
打表,每个点跟每个圆的位置关系,如果两点同在圆外或同在圆内,则不需要跨过栅栏,否则需要跨过。

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int maxn=1e3+10;
ll x1[maxn],x2[maxn],y11[maxn],y2[maxn],r[maxn];
bool vis[maxn][maxn];
int main()
{
    ll n,m,k;
    int x,y;
    scanf("%lld%lld%lld",&n,&m,&k);
    for(int i=1;i<=n;i++)scanf("%lld%lld",&x1[i],&y11[i]);
    for(int i=1;i<=m;i++)scanf("%lld%lld%lld",&r[i],&x2[i],&y2[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            ll d=(x1[i]-x2[j])*(x1[i]-x2[j])+(y11[i]-y2[j])*(y11[i]-y2[j]);
            if(d<r[j]*r[j])vis[i][j]=1;
        }
    }
    while(k--)
    {
        scanf("%d%d",&x,&y);
        int ans=0;
        for(int i=1;i<=m;i++)
        {
            if(vis[x][i]!=vis[y][i])ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

G-Fire Again
BFS 队列最后一个就是答案,多点起火就在初始队列时,多放几个点进去就行了
文件输入加
freopen(“input.txt”,“r”,stdin);
freopen(“output.txt”,“w”,stdout);
就行

#include<bits/stdc++.h>
using namespace std;
const int maxn=2010;
struct node
{
    int x,y;
};
bool vis[maxn][maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int main()
{
    freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
    int n,k,x,y,m;

    scanf("%d%d%d",&n,&m,&k);
    node p,ans,pp;
    queue<node> q;
    while(k--)
    {
        scanf("%d%d",&x,&y);
        p.x=x;
        p.y=y;
        q.push(p);
        vis[x][y]=1;
    }
    while(!q.empty())
    {
        ans=p=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            pp.x=p.x+dx[i];
            pp.y=p.y+dy[i];
            if(pp.x>0 && pp.x<=n && pp.y>0 && pp.y<=m && !vis[pp.x][pp.y])
            {
                vis[pp.x][pp.y]=1;
                q.push(pp);
            }
        }
    }
    cout<<ans.x<<" "<<ans.y<<endl;
    return 0;
}

H-Mysterious Present
最长上升子序列变形
先把能装卡片的信封都跳出来,再排个序,后面按最长上升子序列求一下就行了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e3+11;
struct node
{
    int x,y,id;
    bool operator <(const node &k)const{
        return x>k.x;
    }
}e[maxn];
int dp[maxn][3];
int main()
{
    int x,h,w,y,z,cnt=0,n;
    scanf("%d%d%d",&n,&h,&w);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&y,&z);
        if(y>h && z>w) //把能装卡片的信封挑出来
        {
            e[++cnt].x=y;
            e[cnt].y=z;
            e[cnt].id=i;
        }
    }
    if(cnt==0)puts("0");
    else
    {
        dp[1][0]=1;
        sort(e+1,e+cnt+1);
        for(int i=2;i<=cnt;i++)
        {
            for(int j=1;j<i;j++)
            {
                if(e[i].x<e[j].x && e[i].y<e[j].y && dp[i][0]<dp[j][0])
                {
                    dp[i][1]=j;
                    dp[i][0]=dp[j][0];
                }
            }
            dp[i][0]++;
        }
        int ans=1;
        for(int i=1;i<=cnt;i++)
        {
            if(dp[i][0]>dp[ans][0])ans=i;
        }
        printf("%d\n",dp[ans][0]);
        queue<int> s;
        s.push(e[ans].id);
        while(dp[ans][1])
        {
            ans=dp[ans][1];
            s.push(e[ans].id);
        }
        while(!s.empty())
        {
            cout<<s.front()<<" ";
            s.pop();
        }
    }
    return 0;
}

I-Looking for Order
一个人在起点,有许多物品散落在各个地方,现在给出人起点的坐标和物品的坐标,然后给出一个要求,每次最多只能拿两个物品拿完物品必须回到原点装到包里面,求最短路程的方案,答案和路径都要输出。

这题如果是超级暴力即状态和两个点都全部枚举会超时,稍微优化下,因为先取哪个都是一样的,因为都 要回到原点,这样相当于每次是从原地出发找出然后回去,所以首先枚举状态,然后枚举第一个点,这时候先转移这个点的状态,然后再枚举第二个点,当枚举的第二个点都转移完状态后,就跳出第一个点的循环。

#include<bits/stdc++.h>
using namespace std;
const int maxn=(1<<24)+10;
int dp[maxn],pre[maxn],ans[100],x[30],y[30],mp[30][30];
int main()
{
    int x0,y0,n,cnt=0;
    cin>>x0>>y0>>n;
    for(int i=0;i<n;i++)
    {
        cin>>x[i]>>y[i];
    }
    x[n]=x0,y[n]=y0;
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            mp[i][j]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
        }
    }
    memset(dp,-1,sizeof(dp));
    dp[0]=0;
    for(int i=0;i< 1<<n ;i++)
    {
        if(dp[i]!=-1)
        {
            for(int j=0;j<n;j++)
            {
                if(!(i&(1<<j)))
                {
                    int tmp=dp[i]+2*mp[n][j];
                    int t=(i|(1<<j));
                    if(dp[t]==-1 || dp[t]>tmp)
                    {
                        dp[t]=tmp;
                        pre[t]=i;
                    }
                    for(int k=0;k<n;k++)
                    {
                        if(!(t&(1<<k)))
                        {
                            int t1=t|(1<<k);
                            tmp=dp[i]+mp[n][j]+mp[j][k]+mp[k][n];
                            if(dp[t1]==-1 || dp[t1]>tmp)
                            {
                                dp[t1]=tmp;
                                pre[t1]=i;
                            }
                        }
                    }
                    break;
                }
            }
        }
    }
    x0=(1<<n)-1;
    cout<<dp[x0]<<endl;
    int pr,tmp,a,b;
    while(x0)
    {
        pr=pre[x0];
        tmp=pr^x0;
        a=b=0;
        for(int i=0;i<n;i++)
        {
            if(tmp&(1<<i))
            {
                b=a;
                a=i+1;
            }
        }
        ans[++cnt]=0;
        ans[++cnt]=a;
        if(b)
        {
            ans[++cnt]=b;
        }
        x0=pr;
    }
    ans[++cnt]=0;
    for(int i=1;i<=cnt;i++)cout<<ans[i]<<" ";
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值