暑假训练第二周周报

总体学习情况

这周时间大多花在写上周的堆栈的题单了,然后比赛又碰到了一些新的知识点,比如无权二分图的最大匹配,01背包的相似例题,但是感觉数据结构的基础还是得练,遇到一些题还是没办法写对。

知识点模块

1.无权二分图最大匹配
用通俗的话来讲,假如有几个男的和几个女的存在暧昧关系,其中有多个男的和多个女的存在暧昧关系也有多个 女的和多个男的存在暧昧关系,比如喜羊羊和美羊羊、红太狼存在暧昧关系,美羊羊和沸羊羊、喜羊羊存在暧昧关系,等等,让你找最多的配对数,也就是让你找最多的1v1的关系。

struct augment_path {
  vector<vector<int> > g;
  vector<int> pa;  // 匹配
  vector<int> pb;
  vector<int> vis;  // 访问
  int n, m;         // 两个点集中的顶点数量
  int dfn;          // 时间戳记
  int res;          // 匹配数

  augment_path(int _n, int _m) : n(_n), m(_m) {
    assert(0 <= n && 0 <= m);
    pa = vector<int>(n, -1);
    pb = vector<int>(m, -1);
    vis = vector<int>(n);
    g.resize(n);
    res = 0;
    dfn = 0;
  }

  void add(int from, int to) {
    assert(0 <= from && from < n && 0 <= to && to < m);
    g[from].push_back(to);
  }

  bool dfs(int v) {
    vis[v] = dfn;
    for (int u : g[v]) {
      if (pb[u] == -1) {
        pb[u] = v;
        pa[v] = u;
        return true;
      }
    }
    for (int u : g[v]) {
      if (vis[pb[u]] != dfn && dfs(pb[u])) {
        pa[v] = u;
        pb[u] = v;
        return true;
      }
    }
    return false;
  }

  int solve() {
    while (true) {
      dfn++;
      int cnt = 0;
      for (int i = 0; i < n; i++) {
        if (pa[i] == -1 && dfs(i)) {
          cnt++;
        }
      }
      if (cnt == 0) {
        break;
      }
      res += cnt;
    }
    return res;
  }
};

把点放进去具体实现需要以下几步

augment_path ans(n,n);//初始化这个
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
             ans.add(i,j);//根据题意去放点
        }
    }
    cout<<ans.solve()<<endl;

2.单调栈的使用
顾名思义,就是栈里存单调递增或单调递减的数。
具体的作用:可以用来找一个数左边第一个大于或第一个小于该数的数

while(st.size()&&st.top()<x)
	{
		st.pop();
	}
	st.push(x);

例题可以参考 区区区间间间

3.初步学习了01背包
模型大概就是,有一个容量为N的背包,有多个物品各自的体积为\(vi\),各自的价值为 \(wi\),然后要求你得出放入的总价值最大是多少。

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 1005;
int v[MAXN];    // 体积
int w[MAXN];    // 价值 
int f[MAXN][MAXN];  // f[i][j], j体积下前i个物品的最大价值 

int main()
{
    int n,m; cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(j<v[i]) f[i][j]=f[i-1][j];
            else{
                f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);    

            }
        }
    }
    cout<<f[n][m];
    
}

4.巧用multiset来做一些维护最大值或最小值的题
比如这一题就是用双指针和multiset来找字符串区间最小的字符
B - Reserve or Reverse

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
typedef pair<char,int>pci;
#define x first
#define y second
#define all(v) v.begin(),v.end()
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};


signed main()
{
     ios::sync_with_stdio(0),cin.tie(0);
    int n; cin>>n;
    string s; cin>>s;
    multiset<char>se;
    for(int i=0;i<n;i++) se.insert(s[i]);
    
    int l=0,r=n-1;
    while(l<r)
    {
        //先检查一下s【l】是不是l-r这段区间的最小字母
        //如果不是,找一个离r最近的并且最小的字母与之交换
        if(s[l]!=*se.begin())
        {
            while(s[r]!=*se.begin()){//如果不是就从set里删掉,维护区间最小字母
                se.erase(se.find(s[r]));//每一次移动都要记得更新区间,达到维护区间最小的目的
                r--;
            }
            swap(s[l],s[r]);
            se.erase(se.find(s[l]));
            l++;
            se.erase(se.find(s[r]));
            r--;
        }else{ 
            se.erase(se.find(s[l]));
            l++;
        }
    }
    cout<<s<<endl;
    
    
    return 0;
}

最后其实这周还有很多题没补完,因为题单总也是要刷完的,所以只能慢慢去补了。

posted on 2024-07-21 17:34  swj2529411658  阅读(41)  评论(0编辑  收藏  举报

导航