分类 刷题 下的文章

#include <iostream>
#include <set>
using namespace std;
int main()
{
    int n;
    cin >> n;
    set<int> s;
    s.insert(0);
    for (int i = 0; i < n; i ++)
    {
        int a;
        cin >> a;
        if (a < (*s.rbegin()))
        {
            s.erase(s.upper_bound(a));
        }
        s.insert(a);
    }
    cout << s.size() - 1 << endl;
    return 0;
}

启示:

用到了set的特性。除了去重之外,还有一个自动按照从小到大排序,和map一样,因为底层是红黑树,一种平衡二叉搜索树。所以可以有rbegin()取最后一个元素,就是最大元素。这里的逻辑就是让每一个轨道都保持一个单调上升的排序,这样再出去的时候就可以按照从大到小排序的出去(从右往左看)。所以对于每一个数来说如果比最大的还要大就单独开一个序列,如果小于最大的,说明可以找到一个序列使得放进去之后组成的排列时单调上升的,为了可以用最少的轨道,这个放进去的序列最好是最后一个数刚好大于要放进去的数,这里使用到了贪心的思想。为了维护每一个单调上升的序列,只要维护一个序列的最后一个值就好,因为前面的值一定比他大,而且不会拿来做比较,所以可以不看,因此set中实际上是维护了一个所有存在的轨道的序列中的最小值的一个不重复集合。而对于要放入的数来说,可以用upper_bound来找到第一个大于他的数,这个数就是要放入的轨道的最后一个值,将当前数放进去,本质上就是在set中将这个最后的值替换。而因为set中没有索引,但是可以自动排序,根据这一特性,为了完成替换这个操作,我们可以将这个最后一个值在set中删去,最后再将a放去set即可,这就在形式上和另外一种情况保持了统一。最后为了保证形式上的统一,不对第一个数特判,即set为空的情况,可以单独放入一个元素0,因为0一定比所有1到n的数小,所以不会被替换,就不会影响答案。最后的答案要求输出轨道的数量,其实就是set中元素的数量,因为有0的存在,所以实际上的答案是s.size() - 1.

题目:

火车站的列车调度铁轨的结构如下图所示。

188

两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有N条平行的轨道。每趟列车从入口可以选择任意一条轨道进入,最后从出口离开。在图中有9趟列车,在入口处按照{8,4,2,5,3,9,1,6,7}的顺序排队等待进入。如果要求它们必须按序号递减的顺序从出口离开,则至少需要多少条平行铁轨用于调度?

输入格式:

输入第一行给出一个整数N (2 ≤ N ≤105),下一行给出从1到N的整数序号的一个重排列。数字间以空格分隔。

输出格式:

在一行中输出可以将输入的列车按序号递减的顺序调离所需要的最少的铁轨条数。

输入样例:

9
8 4 2 5 3 9 1 6 7

输出样例:

4

url: http://oj.ecustacm.cn/problem.php?id=1455

tag: 搜索

思路:使用bfs或者dfs。如果使用bfs需要将每次的路径也放入队列中,当搜到终点时,就直接将答案输出。使用dfs需要使用一种记忆化的剪枝技巧,每次判断当前的路径长度 + 1和即将到达的那个点的到那个点的最短的长度比较,如果大于就直接跳过,如果小于就更新那个点的值。这种做法含有记忆化搜索的思想。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
char d[31][51];
char r[] = {'D', 'U', 'L', 'R'};
int nx[] = {1, -1, 0, 0};
int ny[] = {0, 0, -1, 1};
int minv = 0x3f3f3f3f;
string res = "";
bool st[31][51];
int mastep[31][51];
void dfs(int len, int x, int y, string ans)
{
    if (x == 30 && y == 50)
    {
        if (len == minv)
        {
            res = min(res, ans);
        }
        if (len < minv)
        {
            minv = len;
            res = ans;
        }
        return;
    }
    for (int i = 0; i < 4; i ++)
    {
        int xx = x + nx[i], yy = y + ny[i];
        if (xx < 1 || xx > 30 || yy < 1 || yy > 50 || st[xx][yy] || d[xx][yy] == '1') continue;
        if (len + 1 > mastep[xx][yy]) continue;
        if (len >= minv) continue;
        st[xx][yy] = true;
        mastep[xx][yy] = len + 1;
        dfs(len + 1, xx, yy, ans + r[i]);
        st[xx][yy] = false;
    }
}
int main()
{
    memset(mastep, 0x3f, sizeof mastep);
    for (int i = 1; i <= 30; i ++)
        for (int j = 1; j <= 50; j ++)
            cin >> d[i][j];
    dfs(0, 1, 1, "");
    cout << res << endl;
    return 0;
}

答案:

#include <iostream>
using namespace std;
int main()
{
    cout << "DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR" << endl;
}

url: https://ybt.ssoier.cn/problem_show.php?pid=1638

tag:
裴蜀定理,拓展欧几里得算法,数论

思路:
根据题意可以得出,y - x = b d mod n ⇒ b d - a n = y - x。所以可以先求出n与d的最大公约数,如果y - x 不能整除,说明不能到达,如果可以整除再求最小的b。用拓展欧几里得算法可以求出 x n + y d = (n,d) 的一组解,然后令该式两边同乘 (y - x)/ (n,d)可以将该式变为b d - a * n = y - x。最后求最小的d就是让其取模n / gcd的正余数。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
int main()
{
    int T;
    cin >> T;
    while (T --)
    {
        LL n, d, x, y, a, b;
        scanf("%lld%lld%lld%lld", &n, &d, &x, &y);
        LL gcd = exgcd(n, d, a, b);
        if ((y - x) % gcd) cout << "Impossible\n";
        else
        {
            b *= (y - x) / gcd;
            n /= gcd;
            printf("%lld\n", (b % n + n) % n);
        }
    }
    return 0;
}

url: http://oj.ecustacm.cn/problem.php?id=1369

tag:
模拟

思路:
求字节的二进制表示,其实就是求每个数的补码。对于正数来说,补码就是原码。对于负数来说,先是对原码求反码然后再加1.对于-128来说比较特殊,补码是10000000,通过正常的求法求不出,所以需要特殊判断。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
int num[10];

void resoult(int a)
{
    int j = 8;
    while(a)
    {
        num[j --] = a % 2;
        a /= 2;
    }
}
void inverse()
{
    for (int i = 2; i <= 8; i ++)
    {
        if (num[i] == 0) num[i] = 1;
        else num[i] = 0;
    }
    num[8] += 1;
    for (int i = 8; i >= 3; i --)
    {
        num[i - 1] += num[i] / 2;
        num[i] %= 2;
    }
}
void binary(int a)
{
    if (a == -128)
    {
        cout << "1 " << endl;
        return;
    }
    memset(num, 0, sizeof num);
    if (a >= 0)
    {
        num[1] = 0;
        resoult(a);
    }
    else
    {
        num[1] = 1;
        resoult(abs(a));
        inverse();
    }
}
void print()
{
    for (int i = 1; i <= 8; i ++)
    {
        if (num[i]) cout << 1;
        else cout << ' ';
    }
}
int main()
{
    for (int i = 0; i < 10; i ++)
    {
        for (int j = 0; j < 16; j ++)
        {
            int a, b;
            cin >> a >> b;
            binary(a);
            print();
            binary(b);
            print();
            cout << endl;
        }
        cout << endl;
    }
    return 0;
}

简洁版:

#include <iostream>
#include<cstring>
using namespace std;
int num[10];
void print()
{
    
    for(int i=1;i<=8;i++) printf("%c",num[i]==1?"1":" ");
}
int main()
{
    
    int a,b;
    for(int i=1;i<=10;i++)
    {
    
        for(int j=1;j<=16;j++)
        {
    
            cin>>a>>b;
            memset(num,0,sizeof(num ));
            for(int k =8;k>=1;i--) num[k]&=a,a>>=1;
            print();
             memset(num,0,sizeof(num ));
            for(int k =8;k>=1;k--) num[k]&=b,b>>=1;
            print();
            cout<<endl;
        }
        cout<<endl;
    }
    return 0;
}

结果:

     1
     1
     1    1
111111111111
     1    1
     1    1
     1    1
     1    1
     1    1
    1     1
    1     1
   1      1   1
   1      1   1
  1        1111
11

   1     1
   1     1
  1   1  1   1
 1111111 111111
 1    1 1    1
 1    11     1
 1    1      1
 1    1 1    1
 111111  11  1
 1    1   1  1
 1    1      1
 1    1      1
 1    1      1
 111111      1
 1    1   1 1
           1

     1
     1
     1
     1    1
111111111111
     1    1
     1    1
     1    1
     1    1
     1    1
    1     1
    1     1
   1      1   1
   1      1   1
  1        1111
11

        1

 1      1
 1
  11    1
  11
   1   1    1
       1111111
      1     1
    1    1 1
   1     1
  1      1
111      1
  1     1 1
  1     1 1
  1    1   1
  1   1     1
  1  1      111
  1 1        1

     1
      11
       1
             1
111111111111111
     1
     1     1
     11111111
     1     1
     1     1
     1     1
    1      1
    1      1
   1       1
  1     1 1
 1       1

   1     1
   1 1   1  1
  11111 111111
 1  1  1  1
     1 1   1
       1
  11111111111
       1
111111111111111
         1
         1 1
  11111111111
    1    1
     1   1
       1 1
        1


           1
  11111111111
       1
       1
       1
       1     1
111111111111111
       1
       1
       1
       1
       1
       1
       1
     1 1
      1

      1
      1
     1111111
    1     1
   11    1
  1  1 11
  1  1 1
      1 1
      1
    11  1
    11
 111   1111111
      1     1
    11     1
   1  1   1
  1    111
       1
    111
 111

       1
       1
       1
    1  1  1
    1  1   1
   1   1    11
   1   1     1
  1    1   1
 1     1   1
       1  1
       1 1
        1

       1
      1
    11
 111




     1111111
   11      11
  11        11
  111       11
          111
        111
        11
        1



       11
       1
      1111
       11
       1


答案:

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int main()
{
    printf("%.0f", pow(9, 9));
    return 0;
}

url: https://www.luogu.com.cn/problem/P4095

tag:
动态规划,背包DP,进制,枚举

思路:
多重背包问题为基础,做两次01背包,前一次后一次,之后对于每次询问的id就跳过那个id求可能的最大值。

代码:

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <algorithm>  
using namespace std;  
typedef long long LL;  
const int N = 100010;  
struct node{  
    int id;LL s;  
}w[N], v[N];  
LL f1[N][1010], f2[N][1010];  
int idx, m, n;  
int main()  
{  
    cin >> n;  
    for (int i = 1; i <= n;  i++)  
    {  
        int cw, cv, c;  
        cin >> cw >> cv >> c;  
        int now = 1;  
        while (now <= c)  
        {  
            w[++idx].s = cw * now, v[idx].s = cv * now;  
            w[idx].id = i, v[idx].id = i;  
            c -= now, now *= 2;  
        }  
        if(c) {  
            w[++idx].s = cw * c, v[idx].s = cv * c;  
            w[idx].id = i, v[idx].id = i;  
        }  
    }  
    cin >> m;  
    n = idx;  
    for (int i = 1; i <= n; i ++)  
    {  
        for (int j = 0; j <= 1000; j ++) f1[i][j] = f1[i - 1][j];  
        for (int j = 1000; j >= w[i].s; j --)  
        {  
            f1[i][j] = max(f1[i][j], f1[i - 1][j - w[i].s] + v[i].s);  
        }  
    }  
    for (int i = n; i >= 1; i --)  
    {  
        for (int j = 0; j <= 1000; j ++) f2[i][j] = f2[i + 1][j];  
        for (int j = 1000; j >= w[i].s; j --)  
        {  
            f2[i][j] = max(f2[i][j], f2[i + 1][j - w[i].s] + v[i].s);  
        }  
    }  
    for (int k = 1; k <= m; k ++)  
    {  
        int cn, V;  
        cin >> cn >> V;  
        cn ++;  
        LL ans = 0;  
        int l = 0, r = 0;  
        while (w[l + 1].id < cn && l < n) ++ l;  
        r = l;  
        while (w[r + 1].id <= cn && r < n) ++ r;  
        for (int j = 0; j <= V; j++)  
        {  
            ans = max(ans, f1[l][j] + f2[r + 1][V - j]);  
        }  
        cout << ans << endl;  
    }  
    return 0;  
}