acm之旅--湖南大学保研训练赛6

A - Launch of Collider

题目链接:A - Launch of Collider

  • 思路:水题。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAX = 200005;
const int INF = 1<<29;
int a[MAX];
char s[MAX];

int main()
{
    int n;
    scanf("%d", &n);
    scanf("%s", s);
    int Min = INF;
    for(int i=0; i<n; i++)
    {
        scanf("%d", &a[i]);
        if(i==0) continue;
        if(s[i]=='L' && s[i-1]=='R')
        {
            int t = (a[i]-a[i-1])/2;
            if(Min>t) Min = t;
        }
    }
    if(Min==INF) printf("-1\n");
    else         printf("%d\n", Min);
    return 0;
}
B - One Bomb

题目链接:B - One Bomb

  • 思路:原来的思路过于复杂,代码太臃肿。虽然过了,但是思维提升空间很大。网上的思路较为精简,首先记录每一行每一列的墙的数目,然后在遍历每一个结点,如果该结点的同行和同列的墙的数目之和减去(如果该位置时墙则为1,否则为0),等于墙的总数,则YES,当前位置则为输出,不存在则NO。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

string G[1005];
int row[1005], col[1005];

int main()
{
    int n, m, sum = 0;
    cin >> n >> m;
    memset(row, 0, sizeof(row));
    memset(col, 0, sizeof(col));
    for(int i=1; i<=n; i++)
    {
        cin >> G[i];
        G[i] = " "+G[i];
        for(int j=1; j<=m; j++)
        {
            if(G[i][j]=='*')
            {
                sum++;
                row[i]++;
                col[j]++;
            }
        }
    }
    int x = -1, y = -1;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            int cnt = row[i]+col[j];
            if(G[i][j]=='*') cnt--;
            if(cnt==sum)
            {
                x = i, y = j;
                break;
            }
        }
        if(x!=-1) break;
    }
    if(x==-1) cout << "NO" << endl;
    else
    {
        cout << "YES" << endl;
        cout << x << " " << y << endl;
    }
    return 0;
}
C - Vacations

题目链接:C - Vacations

  • 思路:这道DP题的状态定义的好特别。
    • 状态构造:dp[i][k]表示在第i天选择第k个选择的最小休息天数。k=0,1,2。
    • 终态:min(dp[n][0], min(dp[n][1], dp[n][2]))。
    • 状态转移方程:
      • dp[i][1]=min(dp[i-1][0],dp[i-1][2]) , a[i]==1||3 ,否则为INF
      • dp[i][2]=min(dp[i-1][0],dp[i-1][1]) ,a[i]==2||3,否则为INF
      • dp[i][0]=min(dp[i-1][0],dp[i-1][1],dp[i-1][2])+1, 任何情况

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int INF = 1<<29;
const int MAX = 200;
int a[MAX], dp[MAX][5];

int main()
{
    int n;
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d", &a[i]);
    }
    memset(dp, 0, sizeof(dp));
    for(int i=1; i<=n; i++)
    {
        dp[i][0] = min(dp[i-1][0], min(dp[i-1][1], dp[i-1][2]))+1;
        if(a[i]==1 || a[i]==3)
            dp[i][1] = min(dp[i-1][0], dp[i-1][2]);
        else
            dp[i][1] = INF;
        if(a[i]==2 || a[i]==3)
            dp[i][2] = min(dp[i-1][0], dp[i-1][1]);
        else
            dp[i][2] = INF;
    }
    printf("%d\n", min(dp[n][0], min(dp[n][1], dp[n][2])));
    return 0;
}
D - Fix a Tree

题目链接:D - Fix a Tree
参考博文:Codeforces Round #363 (Div. 2) D. Fix a Tree —— 并查集

  • 思路:本题实质上是将无向图转换为一棵树,对于树一定没有环,且是一个连通图。而输入是一些结点的父结点,一个结点有且仅有一个父结点。所以仅有两种情况的环,一个是自身是自己的父结点,一个是自己的子孙结点是自己的父结点,且不会有两个相连的环。
    所以可以使用并查集找出各个集合的根,如果存在某个集合的根的原始父结点是自身,则可以将该结点作为整个树的根(因为不需要改该结点的父节点了),然后将其他集合的根的父节点更改为该结点。如果不存在这样的结点,则选出一个集合的根,将所有集合的根(包括该结点)的父结点更改为该结点。这样一定可以去除环,并形成一个连通图。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAX = 200005;
int father[MAX], a[MAX];
void Init(int n)
{
    for(int i=1; i<=n; i++)
        father[i] = i;
}
int findRoot(int x)
{
    int r = x;
    while(father[r]!=r)
    {
        r = father[r];
    }
    int j = x, i;
    while(j!=r)
    {
        i = father[j];
        father[j] = r;
        j = i;
    }
    return r;
}
void join(int x, int y)
{
    int fx = findRoot(x), fy = findRoot(y);
    if(fx!=fy)
        father[fx] = fy;
}
bool same(int x, int y)
{
    return findRoot(x)==findRoot(y);
}
int main()
{
    int n;
    scanf("%d", &n);
    Init(n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d", &a[i]);
        if(!same(i, a[i]))
            join(i, a[i]);//一定是a[i]作为i的父亲
    }
    int cnt = 0, root = 0;//cnt是改变的次数,root是根节点
    for(int i=1; i<=n; i++)//选出根节点
    {
        if(father[i]==i && a[i]==i)
        {
            root = i;
            break;
        }
    }
    for(int i=1; i<=n; i++)
    {
        if(father[i]==i)
        {
            if(!root)
                root = i, a[i] = i, cnt++;
            else if(i!=root)
                a[i] = root, cnt++;
        }
    }
    printf("%d\n", cnt);
    for(int i=1; i<=n; i++)
    {
        if(i>1)
            printf(" ");
        printf("%d", a[i]);
    }
    printf("\n");
    return 0;
}
E - LRU

题目链接: E - LRU

F - Limak and Shooting Points

题目链接:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值