9.12题解

不算很难,但环境问题卡死一堆人。

T1

众所周知,一场比赛需要一道签到题。

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
string s;
int main()
{
    cin >> s;
    cout << count(s.begin(), s.end(), '1');
    return 0;
}

T2

我们考虑选优惠券时要考虑的参数:

  1. 是否过期
  2. 价格可用
  3. 没有用过

很明显,1 的判断对程序效率影响最大。

我们发现,如果某一张优惠券在某一时刻过期了,

那么在这之后它也一定是过期的。

所以如果我们发现了一张过期优惠券,那它肯定没用了,扔掉就行了。

然后要用优惠券的时候,因为过期的都扔掉了,就不需要重复判断它们。


这题用链表是最好的,但用队列足够了。

把优惠券依次压到队里,每到下一个时刻就把过期的弹出去。

#include <iostream>
#include <utility>
using namespace std;
int h, t, n;bool vis[100050];pair<int, int> a[100050];long long ans;
int main()
{
    cin >> n;
    for(int i = 0, p, q, f;i < n;++i)
    {
        cin >> f >> p >> q;
        while(h < t && a[h].second < q) ++h;
        if(f)
        {
            bool flag = 0;
            for(int j = h;j < t;++j)
                if(!vis[j] && a[j].first >= p)
                {
                    vis[j] = 1;flag = 1;break;
                }
            if(!flag) ans += p;
        }
        else a[t++] = make_pair(p, q + 45), ans += p;
    }
    cout << ans;
    return 0;
}

T3

几乎是裸的完全背包虽然我考场上没想出来,只是建模会有些难。

我们先想一想,一个纪念品,是通过什么赚钱的?

答:从一个低价买入,然后在某个高价时卖出,赚中间的差价。

那么光是这样是写不了 dp 的,因为无法判断要从哪个低价转移到高价。

那如果买了一个东西,第二天马上卖掉呢?

这样是不会影响结果的,因为第二天卖掉之后可以立刻买回来。

然后在第三天再全卖掉,买新的,以此类推……

然后我们就可以开始构造背包模型了。


很明显,背包容量=当前手上剩下的钱

重量=纪念品当天的价格

那价值怎么算呢?我们先看看钱是怎么赚的。

上面说了,钱是差价赚出来的。

所以价值=纪念品下一天的价格 - 纪念品当天的价格

有人就会担心了:如果差价是负的咋办?

凉拌。要是负的背包根本不会考虑它。

为了让代码看着简单点,我用宏定义来表示这几个值:

#include <iostream>
#include <cstring>
#define w(i) p[k][i]
#define v(i) p[k + 1][i] - p[k][i]
using namespace std;
int t, n, m, p[150][150], dp[100050];
int main()
{
    cin >> t >> n >> m;
    for(int i = 1;i <= t;++i)
        for(int j = 1;j <= n;++j)
            cin >> p[i][j];
    for(int k = 1;k < t;++k)
    {
        memset(dp, 0, sizeof dp);
        for(int i = 1;i <= n;++i)
            for(int j = w(i);j <= m;++j)
                dp[j] = max(dp[j], dp[j - w(i)] + v(i));
        m += dp[m];
    }
    cout << m;
    return 0;
}

T4

最短路,奇偶性。虽然我考场上也没想出来

我们知道,题目大意就是给定 $q$ 组 $a$ 和 $l$,

让你判断从 $a$ 开始走 $l$ 步能不能到 $1$。

那么,如果走 $l$ 步能到,那么走 $l+2$ 步肯定也能到(中间徘徊一下)

同理,走 $l+4,l+6……l+2^k$ 肯定都能到。

那么我们可以说:

走 ($≥l$ 且与 $l$ 奇偶性相等) 步肯定能到。

所以,我们要让 $l$ 尽可能小

然后就是最短路了,只不过要分别求奇数偶数,用冰法师方便一些。

注意奇数下一步是偶数,偶数下一步是奇数。

#include <iostream>
#include <queue>
#include <list>
#include <cstring>
#include <utility>
using namespace std;
list<int> edge[100050];int n, m, t, dis0[100050], dis1[100050];
void add(int u, int v) {edge[u].push_back(v);}
void bfs()
{
    queue<pair<int, int> > q;
    for(auto &v : edge[1])
        dis1[v] = 1, q.push(make_pair(v, 1));
    while(!q.empty())
    {
        int u = q.front().first, dep = q.front().second;q.pop();
        for(auto &v : edge[u])
            if(dep % 2) //奇数
            {
                if(dis0[v] > dep + 1) //奇数下一步是偶数
                {
                    dis0[v] = dep + 1;
                    q.push(make_pair(v, dis0[v]));
                }
            }
            else //偶数
            {
                if(dis1[v] > dep + 1) //偶数下一步是奇数
                {
                    dis1[v] = dep + 1;
                    q.push(make_pair(v, dis1[v]));
                }
            }
    }
}
int main()
{
    cin >> n >> m >> t;
    for(int i = 0, u, v;i < m;++i)
        cin >> u >> v, add(u, v), add(v, u);
    memset(dis0, 0x3f, sizeof dis0);dis0[1] = 0;
    memset(dis1, 0x3f, sizeof dis1);bfs();
    for(int i = 0, a, l;i < t;++i)
    {
        cin >> a >> l;
        if(l % 2)
        {
            if(l < dis1[a]) cout << "No" << endl;
            else cout << "Yes" << endl;
        }
        else
        {
            if(l < dis0[a]) cout << "No" << endl;
            else cout << "Yes" << endl;
        }
    }
    return 0;
}
posted @ 2021-09-13 21:01  Jijidawang  阅读(1)  评论(0编辑  收藏  举报  来源