Atcoder Beginner Contest 271 C-F

个人博客地址:

Hexo

C - Manga

题意:给定一个长度为​的序列,每个位置有一个值​,我们可以进行任意多次操作,操作的话是如果序列的长度还大于等于​,那么我们可以任意选择​个数字去删除,然后加进去任意一个数字进去,问我们最后能够得到一个形似​的最长序列

解题思路:首先我们分析一下题意,我们可以知道最后的答案最多不会超过​,那么我们在读入的时候可以将大于​的部分放进一个​里面,然后对于小于​的一部分我们可以先给他标记然后放到一个双端队列里面,如果其中的数字有重复的部分我们也需要放进上面的​之中。接着我们模拟这个过程即可。在模拟的时候注意我们优先取​当中的,然后我们取队列后面的即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
int vis[maxn],n;
int main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin >> n;
    vector<int> a;
    for(int i = 1,x;i <= n;++i)
    {
        cin >> x;
        if(x <= n)
        {
            if(vis[x]) a.push_back(x);
            else vis[x] = 1;
        }
        else a.push_back(x);
    }
    deque<int> q;
    for(int i = 1;i <= n;++i)
    {
        if(vis[i]) q.push_back(i);
    }
    int ans = 0;
    for(int i = 1;i <= n;++i)
    {
        if(vis[i] && q.size())
        {
            ans++;
            q.pop_front();
            continue;
        }
        if(a.size() >= 2)
        {
            a.pop_back();
            a.pop_back();
        }
        else if(a.size() == 1)
        {
            a.pop_back();
            if(!q.empty()) q.pop_back(); 
            else break;
        }
        else
        {
            if(q.size() >= 2)
            {
                q.pop_back();
                q.pop_back();
            }
            else break;
        }
        ans++;
    }
    cout << ans << "\n";
    return 0;
}

D - Flip and Adjust

题意:给定​张卡片,每张卡片背面和正面有一个数字,现在让我们选择每张卡片的正面或者反面,问是否能够组成​

解题思路:首先我们可以观察数据范围,可能是​或者​的​

接下来我们考虑这样的一个过程,首先无论如何每张牌都是要取的,那么假定我现在取到第​张牌的值有​,那么我取第​张牌的时候的值肯定有​和​表示第张牌的正反面的值​

那么我们就可以用​来表示前​张牌可能组成的值

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e2 + 10;
int mp[maxn][2],n,s;
int dp[maxn][40010];
int main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin >> n >> s;
    dp[0][0] = 1;
    for(int i = 1;i <= n;++i)
    {
        cin >> mp[i][0] >> mp[i][1];
        if(i == 1)
        {
            dp[1][mp[i][0]] = 1;
            dp[1][mp[i][1]] = 1;
            continue;
        }
        for(int j = 0;j <= s;++j)
        {
            if(!dp[i - 1][j]) continue;
            if(j + mp[i][0] <= s) dp[i][j + mp[i][0]] = 1;
            if(j + mp[i][1] <= s) dp[i][j + mp[i][1]] = 1;
        }
    }
    if(dp[n][s])
    {
        cout << "Yes\n";
        string ans = "";
        for(int i = n;i >= 1;--i)
        {
            if(s - mp[i][0] >= 0 && dp[i - 1][s - mp[i][0]])
            {
                s -= mp[i][0];
                ans.push_back('H');
            }
            else
            {
                s -= mp[i][1];
                ans.push_back('T');
            }
        }
        assert(ans.length() == n);
        reverse(ans.begin(),ans.end());
        cout << ans << "\n";
    }
    else
    {
        cout << "No\n";
    }
    return 0;
}

E - Subsequence Path

题目大意:给定​个点​条单向边​,没有重边和自环,每个边有一条权值,接下来给定一个序列​,我们走的路径的标号形成的序列必须是这个序列​的子序列,问从点​到点​的最短路径

解题思路:分析题目发现我们的突破口应该在这个给定我们的序列​这里,因为考虑我们取到的序列是这个序列的子序列,这里给我们透露到一些信息,就是假设我们当前已经遍历到第​条边,那么我们是可以用前面​条边来更新这一张图的,那么我们知道这个点之后就可以写啦

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
const int inf = 1e18;
struct node
{
    int u,v,w;
}edge[maxn];
int n,m,k;
signed main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin >> n >> m >> k;
    vector<int> dis(n + 1,inf);
    for(int i = 1,u,v,w;i <= m;++i)
    {
        cin >> u >> v >> w;
        edge[i] = {u,v,w};
    }
    dis[1] = 0;
    for(int i = 1,x;i <= k;++i)
    {
        cin >> x;
        int u = edge[x].u;
        int v = edge[x].v;
        int w = edge[x].w;
        dis[v] = min(dis[v],dis[u] + w);
    }
    if(dis[n] != inf) cout << dis[n] << "\n";
    else cout << -1 << "\n";
    return 0;
}

F - XOR on Grid Path

题目大意:给定一个​的矩阵,我们从点​开始,每次移动可以向​或者​移动,问我们到达​使得异或和为​的路径有多少条

解题思路:首先对于这种矩阵的题目我们肯定先去考虑​的,但是如果暴力去枚举所有方案的话方案数实际会非常的大

根据官方题解的思路,建议我们使用​ ​ ​来做

那​ ​ ​是个什么东西呢?大概就是我从两个点来搜索,搜索到一些具有共同性质的点从而结束,因为这样子会大大减少我们所需要的时间复杂度

那么对于矩阵来说,我们可以从​点和​点来搜索,对于这两个点来说我们都搜索到对角线上面的点来结束,因为对角线刚好分割两个矩阵

那么我们对于​点来说,我们可以直接搜索下去到对角线来记录当前异或和的数值,那么对于​点来说,我们可以往上面搜索,搜到对角线的话就加上之前前面遍历到这一点的值,因为我们需要异或和是​嘛

#include <bits/stdc++.h>
using namespace std;
#define int long long
int mp[30][30],n;
unordered_map<int,int> dp[30][30];
int ans;
void dfs1(int x,int y,int now)
{
    if(x + y == n)
    {
        dp[x][y][now]++;
        return;
    }
    if(x + y > n) return;
    dfs1(x + 1,y,now ^ mp[x + 1][y]);
    dfs1(x,y + 1,now ^ mp[x][y + 1]);
}
void dfs2(int x,int y,int now)
{
    if(x + y == n)
    {
        ans += dp[x][y][now ^ mp[x][y]];
        return;
    }
    if(x + y < n) return;
    dfs2(x - 1,y,now ^ mp[x - 1][y]);
    dfs2(x,y - 1,now ^ mp[x][y - 1]);
}
signed main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin >> n;
    for(int i = 1;i <= n;++i)
    {
        for(int j = 1;j <= n;++j) cin >> mp[i][j];
    }
    dfs1(1,1,mp[1][1]);
    dfs2(n,n,mp[n][n]);
    cout << ans << "\n";
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值