GDUT 寒假训练赛2

本文介绍了GDUT寒假训练赛中的多个编程题目,包括最短路问题、归并排序模拟、并查集应用、贪心策略等。通过详细解析思路,帮助读者理解解题方法。
摘要由CSDN通过智能技术生成

[题目链接:]http://codeforces.com/group/NVaJtLaLjS/contest/236618

A. QAQ(签到题)
题意:
思路:

B. Mike and Shortcuts(最短路)
题意: 从1–n路口之间有n-1条路相连,另外每个路口也许还有一条捷径(单向)通往另外一个路口,问到达每个路口需要花费多少时间
思路: 建立一个图,n-1条双向边,然后捷径视为单向边,求最短路

#include<bits/stdc++.h>
using namespace std;
int n,a[200005],dis[200005],head[200005],vis[200005],cnt;
struct node
{
    int v,next;
}edge[600005];
void add(int u,int v)
{
    edge[++cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}
void bfs()
{
    vis[1]=1;
    queue<int>que;
    que.push(1);
    while(que.size())
    {
        int u=que.front();
        que.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(!vis[v])
            {
                dis[v]=dis[u]+1;
                vis[v]=1;
                que.push(v);
            }
        }
    }
}
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
     {
        add(i,i+1);
        add(i+1,i);
     }
    for(int i=1;i<=n;i++)
        if(i!=a[i])
        add(i,a[i]);
    bfs();
    for(int i=1;i<=n;i++)
    printf("%d ",dis[i]);
    return 0;
}

C. Merge Sort(思维+模拟)
题意: 问一个含1-n的序列经过k次归并排序后是一个有序的序列,求原序列,若分块后的某段序列已经有序,则该段序列结束。
思路: 因为归并排序如同一颗满二叉树有奇数个结点,所以k若为偶数则不成立,先排好1-n,每次交换左分块右边界与右分块的左边界,然后push进队列,每次归并次序+2,直到归并次序为k结束,若是队列为空时小于k,则不满足
注意归并排序(分块的区间均为【l,mid)【mid,r)左闭右开)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N];
int main()
{
    int n,k;
    cin>>n>>k;
    if(!(k%2))
    {
        cout<<"-1"<<endl;
        return 0;
    }
    for(int i=0;i<n;i++)
    {
        a[i]=i+1;
    }
    int ans=1;
    queue<pair<int ,int> >que;
    que.push(make_pair(0,n));
    while(que.size())
    {
        if(ans==k)
            break;
        int l=que.front().first,r=que.front().second;
        que.pop();
        if(l+1==r)
            continue;
        int mid=(l+r)/2;
        swap(a[mid],a[mid-1]);
        ans+=2;
        que.push(make_pair(l,mid));
        que.push(make_pair(mid,r));
    }
    if(ans<k)
        cout<<"-1"<<endl;
    else
    {
        for(int i=0;i<n;i++)
            cout<<a[i]<<" ";
    }
    return 0;
}

D. Roads not only in Berland(并查集)
题意: 有N个城市,共有N-1条双向路,现在问最少取消多少条路,并重新建立新路使得每座城市都可以到达任何一个城市
思路: 并查集求出多少个联通块,在成环的取消一条路,使环断开,连接其他联通块,若求得m个联通块,则需要新建立m-1条新路

#include<bits/stdc++.h>
using namespace std;
int cnt,a[1005],b[1005];
struct node
{
    int x,y;
}c[1005];
int fid(int x)
{
    int r=x;
    while(a[r]!=r)
        r=a[r];
    return r;
}
void edit(int x,int y)
{
    int fx=fid(x);
    int fy=fid(y);
    if(fx==fy)
    {
        c[++cnt].x=x;
        c[cnt].y=y;
    }
    else
        a[fx]=fy;
}
int main()
{
    int n,x,y;
    cin>>n;
    for(int i=1;i<=n;i++)
        a[i]=i;
    for(int i=1;i<n;i++)
    {
        cin>>x>>y;
        edit(x,y);
    }
    cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]==i)
            b[++cnt]=i;
    }
    cout<<cnt-1<<endl;
    for(int i=1;i<cnt;i++)
    {
        cout<<c[i].x<<" "<<c[i].y<<" "<<b[i]<<" "<<b[i+1]<<endl;
    }
    return 0;
}

E. New Year Book Reading(思维+贪心)
题意: 有N本书,以及M天的看书顺序,且书从上到下摞成一叠,问这些书如何叠使得看书所需搬动书最优,也就是搬动书的总质量最小
思路: 按照每本第一次出现的顺序从上往下叠,这样搬动的书的质量最小,就这样从第一天开始贪心

#include<bits/stdc++.h>
using namespace std;
int w[505],book[1005],vis[505],shelf[505];
int main()
{
    int n,m,top=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<=m;i++)
    {
        cin>>book[i];
        if(!vis[book[i]])
        {
            vis[book[i]]=1;
            shelf[++top]=book[i];
        }
    }
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=top;j++)
        {
            if(shelf[j]==book[i]){
            for(int k=j;k>1;k--)
            {
                shelf[k]=shelf[k-1];
            }
            shelf[1]=book[i];
            break;
            }
            ans+=w[shelf[j]];
        }
    }
    cout<<ans<<endl;
    return 0;
}

或者直接用vetor,G.erase(G.begin()+j);表示删除G[ j ]元素,G[ j ]后面的元素往前进一位
G.insert(G.begin(),book[i]); 在G[ 0 ]插入一个元素,原本的元素往后退一位

#include<bits/stdc++.h>
using namespace std;
int w[505],book[1005],vis[505];
vector<int>G;
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<=m;i++)
    {
        cin>>book[i];
        if(!vis[book[i]])
        {
            vis[book[i]]=1;
            G.push_back(book[i]);
        }
    }
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<G.size();j++)
        {
            if(G[j]==book[i]){
            G.erase(G.begin()+j);
            G.insert(G.begin(),book[i]);
            break;
            }
            ans+=w[G[j]];
        }
    }
    cout<<ans<<endl;
    return 0;
}

F. Flipping Game(签到题)
题意: 一个序列只含有1或0 ,一区间内的数被翻转,则0变为1,1变为0,问翻转一次使得这个序列总和最大,则最大为多少
思路: 由于题目数据较小,直接枚举每个可能的区间就行,复杂度O(n^2)


#include <bits/stdc++.h>
using namespace std;
const int Maxn=105;
int num[Maxn],sum[Maxn];
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&num[i]);
		sum[i]=sum[i-1]+num[i];
	}
	int ans=0;
	for (int i=1;i<=n;i++)
	{
		for (int j=i;j<=n;j++)
		{
			int temp=sum[i-1]+sum[n]-sum[j]+(j-i+1)-(sum[j]-sum[i-1]);
			ans=max(ans,temp);
		}
	}
	printf("%d\n",ans);
	return 0;
}

G. Nephren gives a riddle(思维+搜索)
题意: 已知第0句话和第一句话,现给你q组数据,每一组有两个数,一个是第n句话,一个是这个第n句话中的第k个字母,求出这个字母。若不存在第k个字母则输出‘ . ’;
在这里插入图片描述思路: 就是无论是第几句话,都分成五个部分,第一个就是双引号前面的,第二个是双引号中间的,第三个是第一个双引号后面第二个双引号前面的,第四个是第二个双引号,第五个是第二个双引号后面的。根据k判断它在这五个部分中的哪一个,找到后就进行递归搜索
需要注意的是题目限制k<=1e18,在第53句话时就超出1e18了,所以不必需要知道后面的字母,防止字母数爆long long 而出错

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
char f1[]="What are you doing at the end of the world? Are you busy? Will you save us?";
char f2[]="What are you doing while sending \"\"? Are you busy? Will you send \"\"?";
ll len1;
ll len[N];
char tmp[10];
void init()
{
    len[0]=strlen(f1);
    len1=strlen(f2);
    for(int i=1;i<=N;i++)
    {
        if(len[i-1]>1e18){
            len[i]=len[i-1];
        }
        else
            len[i]=len[i-1]*2+len1;
    }

}
char dfs(int n,ll k)
{
    if(n==0)
    {
        if(k<len[0])
            return f1[k];
            return '.';
    }
    if(k<34)
        return f2[k];
    k-=34;
    if(k<len[n-1])
        return dfs(n-1,k);
    k-=len[n-1];
    if(k<32)
        return f2[k+34];
    k-=32;
    if(k<len[n-1])
        return dfs(n-1,k);
    k-=len[n-1];
    if(k<2)
        return f2[k+66];
    return '.';
}
int main()
{
    ll k;
    init();
    int n,q;
    //cout<<len[53]<<endl<<len1;

    cin>>q;
    for(int i=1;i<=q;i++)
    {
        cin>>n>>k;
        k--;
        tmp[i]=dfs(n,k);
    }
    for(int i=1;i<=q;i++)
        cout<<tmp[i];

    return 0;
}

H. Roma and Changing Signs(贪心)
题意: 给出一个序列该序列有正有负,还有可能为0,有k次修改机会,每次修改一个数就是给那个*-1,问经过k次修改后序列最大的和为多少
思路: 这道有个坑点就是一个数可以被变换无数次,而且这k次必须用完,所以我们可以先进行排序,若k小于等于负的个数,取前k个最小的负数乘-1,反之k大于负的个数且序列包含0,则所有负数乘于-1,若序列不含0,看k减去负数的个数后是奇数还是偶数,若是偶数则无影响,若是奇数距离0最近的那个数最终为负数

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int maxd = 1e5+10;
int arr[maxd],n,k,ans,del,flag=1e9;
int main()
{
    // freopen("a.in","r",stdin);
    // freopen("k.out","w",stdout);
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&arr[i]);
        if(arr[i]<=0) del++;
        flag = min(abs(arr[i]),flag);
    }
    if(del > k)
    {
        for(int i=1;i<=k;i++) ans += -arr[i];
        for(int i=k+1;i<=n;i++) ans += arr[i];
    }
    else
    {
        for(int i=1;i<=del;i++) ans += -arr[i];
        for(int i=del+1;i<=n;i++) ans += arr[i];
        ans -= ((k-del)&1)?2*flag:0;
    }
    printf("%d",ans);
    return 0;
}

I. Lucky Tickets(签到题)
题意:
思路:

J. Steps(模拟)
题意: 给出地图的大小与起点,现有k次移动的规律,每次按照该次移动增量移动,直到无法移动,问总共移动几步
思路: 注意一些变量初始化问题就行了

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int main()
{
    ll  n,m;
    cin>>n>>m;
    ll sx,sy;
    cin>>sx>>sy;
    ll k,dx,dy;
    cin>>k;
    ll nx,ny,ans=0;
    while(k--)
    {
        nx=INF,ny=INF;
        cin>>dx>>dy;
        if(dy==0&&dx==0)continue;
        if(dx!=0)
        {
            if(dx>0)
            {
                nx=(n-sx)/dx;
            }
            else if(dx<0){
                nx=(sx-1)/(-dx);
            }
        }
        if(dy!=0)
        {
            if(dy>0)
            {
                ny=(m-sy)/dy;
            }
            else if(dy<0){
                ny=(sy-1)/(-dy);
            }
        }
        ll minn=min(ny,nx);
        if(minn==INF)
            continue;
          sy+=minn*dy;
          sx+=minn*dx;
          //cout<<sx<<endl<<sy;
          ans+=minn;
    }
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值