上海理工大学天梯赛2022补题

目录

二进制

十进制转二进制的巧妙方法

DFS 

DP + 保存路径

Trie树

并查集


2022年天梯赛上海理工大学校内选拔赛【部分题 题解】


二进制

D-Setsuna的K数列

不难发现,K数列本质就是每一个K进制位只能取0或1,这很像二进制,即第n个数就是
把n表示成二进制然后按K进制还原即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 1e7+10,mod=1e9+7;
int a[maxn];
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    LL n, k;  cin>>n>>k;
    LL w = n, ans = 0, t = 1;
    while(w){
        if(w&1){
            ans = (ans+t)%mod;
        }
        t = t*k%mod;
        w >>= 1;
    }
    cout<<ans<<"\n";
    return 0;
}


十进制转二进制的巧妙方法

十进制转二进制(C++)


DFS 

E-Wiki下象棋

思路简单,但是对中国象棋特判的时候,直走一步进行判断的时候,不仅要判断这个位置有没有棋子,还要判断这个位置有没有出界,这一点容易忽略

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;
const int N = 310;

int T, n, m, k, a, b, c, d;
bool map[N][N], vis[N][N];
int dx[8] = {1, 1, -1, -1, 2, 2, -2, -2};
int dy[8] = {2, -2, 2, -2, 1, -1, 1, -1};
int ddx[8] = {0, 0, 0, 0, 1, 1, -1, -1};
int ddy[8] = {1, -1, 1, -1, 0, 0, 0, 0,};
int dist[N][N];

bool check(int x, int y)
{
    if(x <= 0 || y <= 0 || x > n || y > m)  return false;
    return true;
}

int bfs2(int x, int y)//American
{
    queue<PII> q;
    memset(dist, 0, sizeof dist);
    memset(vis, 0, sizeof vis);
    q.push({x, y});
    dist[x][y] = 0;
    vis[x][y] = true;
    
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        
        int distance = dist[t.x][t.y];
        if(t.x == c && t.y == d)    return distance;
        
        for(int i = 0; i < 8; i ++ )
        {
            int xx = t.x + dx[i], yy = t.y + dy[i];
            if(check(xx, yy) && !vis[xx][yy] && !map[xx][yy])
            {
                dist[xx][yy] = distance + 1;
                q.push({xx, yy});
                vis[xx][yy] = true;
            }
        }
    
    }
    return -1;
}

int bfs1(int x, int y)//china
{
    queue<PII> q;
    memset(dist, 0, sizeof dist);
    memset(vis, 0, sizeof vis);
    q.push({x, y});
    dist[x][y] = 0;
    vis[x][y] = true;
    
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        
        int distance = dist[t.x][t.y];
        if(t.x == c && t.y == d)    return distance;
        
        for(int i = 0; i < 8; i ++ )
        {
            int xx = t.x + dx[i], yy = t.y + dy[i];
            int nx = t.x + ddx[i], ny= t.y + ddy[i];
            //难点主要在于这里的边界判断
            //第一行的边界判断好理解
            //第二行是说,有可能走一步也会产生边界问题
            if(check(xx, yy) && !map[xx][yy] && !vis[xx][yy] &&
                 check(nx, ny) && !map[nx][ny])
            {
                dist[xx][yy] = distance + 1;
                q.push({xx, yy});
                vis[xx][yy] = true;
            }
        }
    }
    return -1;
}

int main()
{
    cin >> T ;
    while(T -- )
    {
        memset(map, 0, sizeof map);
        
        cin >> n >> m >> k >> a >> b >> c >> d;

        for(int i = 0; i < k; i ++ )
        {
            int x, y;
            cin >> x >> y;
            map[x][y] = true;
        }
        cout << bfs2(a, b) << " ";
        cout << bfs1(a, b) << endl;
    }
    
    
    return 0;
}


DP + 保存路径

H-叠硬币

如果不用按字典序输出,那么就是一个很经典的线性 dp 问题,转移方程:
        dp[i][j] = min(dp[i-1][j], dp[i-1][j-h[i]] + 1)
其中 dp i,j 表示前 i 个数最少取几个数可以使它们的和正好等于 j 。但现在还需要按照字典
序输出,那么可以将所有 h i 降序排列,在状态转移时优先考虑加入当前的 h i ,即优先转
dp[ i-1][j-h[i] ]+1 ,并记录当前序列的最小数,以方便后续输出。
#include <iostream>
#include <cstring>
#include <algorithm>

#define debug1(val) cout << #val << " = " << val

using namespace std;

const int N = 3010, INF = 0x3f3f3f3f;

int n, H, res;
int h[N], dp[N];

int main()
{
    cin >> n >> H;
    for(int i = 1; i <= n; i ++ ) cin >> h[i];
    sort(h + 1, h + 1 + n);
    
    memset(dp, 0x3f, sizeof dp);
    dp[0] = 0;
    
    for(int i = 1; i <= n; i ++ )
        for(int j = H; j >= h[i]; j -- )//降序排列,优先加入当前i
            dp[j] = min(dp[j], dp[j - h[i]] + 1);
    
    if(dp[H] == INF)  cout << -1 << endl;
    else
    {
        cout << dp[H] << endl;
        int res = dp[H];
        for(int i = 1; i <= n; i ++ )
        {
            if(dp[H - h[i]] == res - 1)
            {
                cout << h[i] << ' ';
                H -= h[i];
                res -- ;
                if(!res)    break;
            }
        }
    }
    
    return 0;
}


Trie树

I-A+B Problem again

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010, M = 2000010;

int n, a[N], cnt[N];
int tr[M][20], idx;

void Insert(int x)
{
    string str = to_string(x);
    while(str.size() < 6)   str = "0" + str;
    int p = 0;
    for(int i = 0; str[i]; i ++ )
    {
        int u = str[i] - '0';
        if(!tr[p][u])   tr[p][u] = ++ idx;
        p = tr[p][u];
        cnt[p] ++ ;//??
    }
}

int query(int x)
{
    string str = to_string(x);
    while(str.size() < 6)   str = "0" + str;
    int p = 0, ans = 0;
    for(int i = 0; str[i]; i ++ )
    {
        int u = 9 - (str[i] - '0');
        while(1)
        {
            if(cnt[tr[p][u]])
            {
                p = tr[p][u];
                ans = ans * 10 + (u + str[i] - '0') % 10;
                break;
            }
            u -- ;
            if(u < 0)   u = 9;
        }
    }
    return ans;
}

void erase(int x)
{
    string str = to_string(x);
    while(str.size() < 6)   str = '0' + str;
    int p = 0;
    for(int i = 0; str[i]; i ++ )
    {
        int u = str[i] - '0';
        p = tr[p][u];
        cnt[p] -- ;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    
    cin >> n;
    for(int i = 0; i < n; i ++ )
    {
        cin >> a[i];
        Insert(a[i]);
    }
    
    for(int i = 0; i < n; i ++ )
    {
        erase(a[i]);
        int res = query(a[i]);
        cout << res << " ";
        Insert(a[i]);
    }
    cout << endl;
    
    
    return 0;
}


并查集

L-剪绳子

由于题目里提示了“不会重复剪同样的位置”,可以考虑将绳子上的 1000001 个(包括
左右两端点)都单独视为一个集合,并将询问记录下来,倒序考虑,一开始根据绳子上
所有被剪过的端点将绳子分成若干部分,并用并查集将这些部分各自合并起来,然后根
据询问的倒序依次处理,若是 "A f" ,则输出 f 所在集合的长度,若是 "C f" ,则合并 f f-1
所在的集合
#include<bits/stdc++.h>
using namespace std;
vector<double>ve;
int n;
int main(void)
{
    cin>>n;
    ve.push_back(0),ve.push_back(10);
    for(int i=0;i<n;i++)
    {
        string op; cin>>op;
        if(op=="C")
        {
            double x; cin>>x;
            ve.push_back(x);
            sort(ve.begin(),ve.end());
        }else
        {
            double x; cin>>x;
            if(x==10) printf("%.5lf\n",10-ve[ve.size()-2]);//最后一个位置
            else
            {
                int index=upper_bound(ve.begin(),ve.end(),x)-ve.begin();
                printf("%.5lf\n",ve[index]-ve[index-1]);
            }
        }
    }
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;
    cin>>t;
    set<int> st;
    st.insert(0);
    st.insert(1000000);
    while(t--){
        char s[2];
        int x,y;
        scanf("%s %d.%d",s,&x,&y);
        int cur=x*100000+y;
        if(s[0]=='C'){
            st.insert(cur);
        }else{
            if(cur==1000000){
                
                int res=1000000-*prev(st.lower_bound(cur));
                printf("%d.%05d\n",res/100000,res%100000);
            }else{
                int res=*st.upper_bound(cur)-*prev(st.upper_bound(cur));
                printf("%d.%05d\n",res/100000,res%100000);
            }
        }
    }
}
#include<bits/stdc++.h>
using namespace std;

int main()
{
    int t;
    cin >> t;
    set<double> s;
    s.insert(0.0);
    s.insert(10.0);
    while(t--)
    {
        char c;
        double x;
        cin >> c >> x;
        if(c=='C')
        {
            s.insert(x);
        }
        else if(c=='A')
        {
            auto it = s.lower_bound(x);
            if(x<=1e-6)
                printf("%.5f\n", *s.upper_bound(x));
            else 
                printf("%.5f\n", *it - *prev(it));
        }
    }
    return 0;
}

posted @ 2022-05-05 08:41  光風霽月  阅读(19)  评论(0编辑  收藏  举报