2022.3.18

蓝书

AcWing 186. 巴士

一开始想的是在搜索的时候再枚举巴士的路线,但是发现搜索的时候处理太麻烦了,应该在读入的时候先预处理出一共可能有的路线,就是枚举每个起点和它的时间间隔,枚举起点只需要从0-30就可以了并不用像题解一样枚举到60,因为是个等差数列,如果你公差都大于30了枚举的下一项肯定已大于60,,必定不合法。如果终点有某个点是没有巴士停的话说明这条线路是不合法的,在枚举完了之后,因为题目已说明最多不会超过17条线路,所以可以迭代加深一层层的搜索,当迭代到限定层数是确定一下当前已经算到的巴士数量是否和给的n相同。在每一层我们依次枚举每一条可能的线路,这里我只想到了检查一下枚举的起点和时间间隔是否合法,但是没有想到题解的剪枝,结果超时。题解的剪枝是说当当前的枚举的路线的巴士数量乘以之后还没有枚举层数加上当前的巴士数量还是小于n的话就需要回溯。如果合法就继续搜索下一层,同时需要将这条路线上经过的时间点的巴士数量依次-1,代表这条路线已经被用过了,最后还原现场的时候记得要加回去。一开始从1-n循环写习惯了,结果题目是从0-59,后面调着调着才发现,下次要注意一点。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=300+10,INF=1e8;
int a[N],n,cnt,dep;
struct node
{
    int st, d, tot;
} bus[N];
bool cmp(node a,node b)
{
    return a.tot > b.tot;
}
bool check(int st, int d)
{
    while (st < 60)
    {
        if(a[st]==0)
            return 0;
        st += d;
    }
    return 1;
}
bool dfs(int x,int dep,int st,int sum)
{
    if(x==dep)
    {
        return sum == n;
    }

    for (int i = st; i <= cnt;i++)
    {
        int sta = bus[i].st, d = bus[i].d;
        if(bus[i].tot*(dep-x)+sum<n)
            continue;
        if(!check(sta,d))
            continue;
        for (int j = sta; j < 60;j+=d)
            a[j]--;
        if(dfs(x+1,dep,i,sum+bus[i].tot))
            return 1;
        for (int j = sta; j < 60;j+=d)
            a[j]++;
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for (int i = 1; i <= n;i++)
    {
        int x;
        cin >> x;
        a[x]++;
    }
    for (int i = 0; i < 30;i++)
    {
        for (int j = i + 1; i + j < 60;j++)
        {
            if(check(i,j))
            {
                bus[++cnt] = {i, j, (59 - i) / j + 1};
            }
        }
    }
    sort(bus + 1, bus + 1 + cnt, cmp);
    while(dep<=17)
    {
        if(dfs(0,dep,1,0))
            break;
        dep++;
    }
    cout << dep;
    return 0;
}

AcWing 187. 导弹防御系统

蓝书提示迭代加深,就先把大体的模板先抄了上去,想的是因为又要有上升还要有下降,枚举的时候肯定两种状态都要枚举,但是如果就是直接枚举每一个序列的每一个位置的话肯定爆炸,必超时,因此这里需要贪心的优化,搞两个数组分别表示每一层上升和下降的数组的最大值,分别看当前这一层能放到哪个序列里,如果能放那就继续搜索下一层,不用增加序列的数量,如果不能的话说明不符合,只能新开一个序列开存放,同意搜索下一层,如果搜到最后等于个数==n时候就回溯。发现一个小细节需要注意,因为一开始搜索是从第0层开始搜的,所以一开始读a[i]的时候i要从0开始,尽量做到一致,还有就是剪枝的顺序问题,因为没把可行性剪枝放到最前面有个点一直过不了,意思就是得先判断当前状态合不合法才继续判断,不然就算你搜到了第n个你的答案也是错的。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=50+10,INF=1e8;
int a[N],b[N],c[N],n;
bool dfs(int dep,int x,int up,int down)
{
    if(up+down>dep)
        return 0;
    if(x==n)
        return 1;
    int cnt = 0;
    while(cnt<up)
    {
        if(a[x]>b[cnt])
            break;
        cnt++;
    }
    int back = b[cnt];
    if(cnt!=up)
    {
        b[cnt] = a[x];
        if(dfs(dep,x+1,up,down))
            return 1;
        b[cnt] = back;
    }
    else
    {
        b[cnt] = a[x];
        if(dfs(dep,x+1,up+1,down))
            return 1;
        b[cnt] = back;
    }

    cnt = 0;
    while(cnt<down)
    {
        if(a[x]<c[cnt])
            break;
        cnt++;
    }
    back = c[cnt];
    if(cnt!=down)
    {
        c[cnt] = a[x];
        if(dfs(dep,x+1,up,down))
            return 1;
        c[cnt] = back;
    }
    else
    {
        c[cnt] = a[x];
        if(dfs(dep,x+1,up,down+1))
            return 1;
        c[cnt] = back;
    }

    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin >> n&&n)
    {
        for (int i = 0; i < n;i++)
        {
            cin >> a[i];
        }
        int dep = 0;
        while(dep<=50)
        {
            if(dfs(dep,0,0,0))
                break;
            dep++;
        }
        cout << dep<<'\n';
        memset(b, 0, sizeof b);
        memset(c, 0, sizeof c);
    }
    return 0;
}

CFAB题特训赛4

A - Madoka and Math Dad

特判一下1和2,对于其他的数,如果能整除3的话为了让数尽可能大应该循环输出21,如果模3余1的话因为不能相邻,所以得是12,同理余2的时候得是21。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        cin >> n;
        if(n==1)
            cout << 1 << '\n';
        else if(n==2)
            cout << 2 << '\n';
        else
        {
            if(n%3==1)
            {
                for (int i = 1; i <= n / 3;i++)
                {
                    cout << "12";
                }
                cout << "1" << '\n';
            }
            else if(n%3==2)
            {
                for (int i = 1; i <= n / 3;i++)
                {
                    cout << "21";
                }
                cout << "2" << '\n';
            }
            else
            {
                for (int i = 1; i <= n / 3;i++)
                {
                    cout << "21";
                }
                cout << '\n';
            }
        }
    }

    return 0;
}

B - Madoka and the Elegant Gift

之前有看到过题解。。判断是否优雅,需要判断这个连通块是否为矩形,如果不是矩形的话说明会有空出来的部分就要输出no。如何判断是否矩形,只需要从(2,2)开始枚举每一个点看以这个点为右下角的2x2的矩形是否有3个点是1,如果是矩形那肯定是4个点都是1。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=100+10,INF=1e8;
char s[N][N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        int n, m;
        cin >> n >> m;
        for (int i = 1; i <= n;i++)
            for (int j = 1; j <= m;j++)
                cin >> s[i][j];
        int f = 0;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if (i >= 2 && j >= 2)
                {
                    int cnt = 0;
                    if(s[i][j]=='1')
                        cnt++;
                    if (s[i - 1][j - 1] == '1')
                        cnt++;
                    if(s[i-1][j]=='1')
                        cnt++;
                    if(s[i][j-1]=='1')
                        cnt++;
                    if(cnt==3)
                    {
                        f = 1;
                        break;
                    }
                }
            }
            if(f) break;
        }
        if(f)
            cout << "no" << '\n';
        else
            cout << "yes" << '\n';
    }

    return 0;
}

C - I Scream

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int a,b;
    cin>>a>>b;
    if(a+b>=15&&b>=8)
        cout << 1;
    else if(a+b>=10&&b>=3)
        cout << 2;
    else if(a+b>=3)
        cout << 3;
    else 
        cout << 4;

    return 0;
}

D - Madoka and Childish Pranks

题意:初始01矩阵的格子均为0,你可以进行若干以下操作,需要使得矩阵变为目标矩阵 。
选定矩阵的一个子矩阵B,将其中的偶数点(元素在B中的行号加列号为偶数,例如B中的第一行第一列)赋值为0 ,奇数点赋值为1。
特判如果需要输出的图案左上角的点是1的话则输出-1,因为任何操作都无法使左上角变成1,对于其他点(i,j),要将该变成1只需要将其选定为以(i,j-1)为左侧的1x2的矩阵即可让该点变成奇数点,从而染成1。如果已经在最左边了那就改成(i-1,j)为上边的1x2矩阵。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=100+10,INF=1e8;
char s[N][N];
int ans[N*100][10];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        int n, m;
        cin >> n >> m;
        for (int i = 1; i <= n;i++)
            for (int j = 1; j <= m;j++)
            {
                cin >> s[i][j];
            }
        if(s[1][1]=='1')
        {
            cout << -1 << '\n';
            continue;
        }
        int cnt = 0;
        for (int i = n; i >= 1;i--)
            for (int j = m; j >= 1;j--)
            {
                if(s[i][j]=='1')
                {
                    if(j>=2)
                    {
                        ans[++cnt][1] = i;
                        ans[cnt][2] = j-1;
                        ans[cnt][3] = i;
                        ans[cnt][4] = j;
                    }
                    else
                    {
                        ans[++cnt][1] = i-1;
                        ans[cnt][2] = j;
                        ans[cnt][3] = i;
                        ans[cnt][4] = j;
                    }
                }
            }
        cout << cnt << '\n';
        for (int i = 1; i <= cnt;i++)
        {
            cout << ans[i][1] << ' ' << ans[i][2] << ' ' << ans[i][3] << ' ' << ans[i][4] << '\n';
        }
    }
    return 0;
}

E - Party

一开始用并查集没做出来,后面看题解是搜索树的最深的深度,不过看代码好像也差不多。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
int p[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n;i++)
        cin >> p[i];
    int ans = 0;
    for (int i = 1; i <= n;i++)
    {
        int sum = 1;
        int x = i;
        while(p[x]!=-1)
        {
            sum++;
            x=p[x];
        }
        ans = max(ans, sum);
    }
    cout << ans;
    return 0;
}

F - Job Assignment

贪心,排序,比较最小,如果最小是同一个人就和第二小比较

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
pii a[N], b[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n;i++)
    {
        cin >> a[i].first;
        cin >> b[i].first;
        a[i].second = i;
        b[i].second = i;
    }
     sort(a + 1, a + 1 + n);
     sort(b + 1, b + 1 + n);
     int ans = 0,ans1=0,ans2=0;
     if(a[1].second==b[1].second)
     {
         ans = a[1].first + b[1].first;
         ans1 = max(a[1].first, b[2].first);
         ans2 = max(a[2].first, b[1].first);
         ans = min({ans, ans1, ans2});
     }
     else
         ans = max(a[1].first, b[1].first);
     cout << ans;
     return 0;
}

G - BerSU Ball

一开始直接二分查找搞错了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=100+10,INF=1e8;
int a[N], b[N],vis[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m,cnt=0;
    cin >> n;
    for (int i = 1; i <= n;i++)
    {
        cin >> a[i];
    }
    cin>>m;
    for (int i = 1; i <= m;i++)
    {
        cin >> b[i];
    }
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + m);
    for (int i = 1,j=1; i <= n&&j<=m;)
    {
        if(abs(a[i]-b[j])<=1)
        {
            cnt++;
            i++;
            j++;
        }
        else if(a[i]<b[j])
        {
            i++;
        }
        else
            j++;
    }
    cout << cnt;
    return 0;
}

H - Squared Error

推导(a-b)^2的公式,然鹅并不会。。看了推导过程

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    ll s1=0,s2 = 0, m = 0;
    for (int i = 0; i < n;i++)
    {
        ll x;
        cin >> x;
        m += x * s1;
        s1 += x, s2 += x * x;
    }
    m <<= 1;
    s2 *= (n - 1ll);
    cout << s2 - m;
    return 0;
}

I - Young Explorers

只有队伍的人数大于等于e的时候才能进队,我们把所有人的e从小到大排序,如果当前队伍的人数大于等于当前点的e的话就加进去,否则sum清零,新建一个队伍。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e5+10,INF=1e8;
int a[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        cin >> n;
        for (int i = 1; i <= n;i++)
        {
            cin >> a[i];
        }
        sort(a + 1, a + 1 + n);
        int sum = 0,cnt=0;
        for (int i = 1; i <= n;i++)
        {
            sum++;
            if(sum>=a[i])
            {
                cnt++;
                sum = 0;
            }
        }
        cout << cnt << '\n';
    }
    return 0;
}

J - Journey

和图并没有什么关系,如果x表示期望,设一次成功的概率为p,则x=1+(1-p)*x。x=1/p。设已经连通的点的集合为s,那一个点被选中的概率为1/(n-s)/n,为n/(n-s),s从1-n-1。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    int n;
    scanf("%d",&n);
    double ans = 0;
    for (int i = 1; i <= n-1; i++)
    {
        ans += (double)n / (n - i);
    }
    printf("%.6lf", ans);
    return 0;
}

K - Mex Min

给定要给长度为n的序列,现在我们从i~i+m - 1这m个数中,找到第一个不存在的非负整数,然后在这n+m-1个值中输出最小值。
看了题解,是说一个长度为m的序列如果里面0-m-1个数字都只出现了一次话那么最小的数就是m,如果0-m-1里有一个数缺少了,那么这个数就是要找到的最小的数,也相当于在一个m+1长度的序列中如果有一个数没有出现过2次的话就说明这个数就是要找的数。于是利用数组记录每个数出现的位置,当扫到这个数时如果这个相邻位置长度大于m的话就直接输出,同时还要注意最后n的情况。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1500000+10,INF=1e8;
vector<int> ans[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n;i++)
    {
        int x;
        cin >> x;
        if(x<=m)
            ans[x].push_back(i);
    }
    for (int i = 0; i <= m;i++)
    {
        if(ans[i].size()==0)
        {
            cout << i << '\n';
            break;
        }
        int f = 0;
        int last = -1;
        for(auto t:ans[i])
        {
            if(t-last>m)
            {
                f = 1;
                break;
            }
            else
            {
                last = t;
            }
        }
        if(n-last>m)
            f = 1;
        if(f)
        {
            cout << i << '\n';
            break;
        }
    }
    return 0;
}

L - Game 23

先判断一下b能否被a整除,如果能整除说明商肯定是包含2和3,于是我们可以分别把2和3的个数都求出来,如果最后的商不为1的话说明除不尽,那么无解。

## #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int a, b;
    cin >> a >> b;
    if(b%a!=0)
    {
        cout << -1;
    }
    else
    {
        int cnt = 0;
        b /= a;
        while(b%2==0)
        {
            cnt++;
            b /= 2;
        }
        while(b%3==0)
        {
            cnt++;
            b /= 3;
        }
        if(b>1)
            cout << -1;
        else
            cout << cnt;
    }

    return 0;
}

M - Increase Subarray Sums

先枚举出每一段长度为i的子段的前缀和,k从0到n,答案就为当前长度为i最大的前缀和的值加我们可以加上x的次数,即min(i,k)*x,因为当前长度为,当k>i其实和k=i是一样的。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=5000+10,INF=1e8;
int n, x,a[N],s[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        cin >> n >> x;
        for (int i = 1; i <= n;i++)
        {
            cin >> a[i];
            s[i] = -1e9;
        }
        for (int i = 1; i <= n;i++)
        {
            int sum = 0;
            for (int j = i; j <= n;j++)
            {
                sum += a[j];
                s[j - i + 1] = max(s[j - i + 1], sum);
            }
        }
        for (int k = 0; k <= n;k++)
        {
            int ans = 0;
            for (int i = 0; i <= n;i++)
            {
                ans = max(ans, s[i] + min(k, i) * x);
            }
            cout << ans << ' ' ;
        }
        cout << '\n';
    }

    return 0;
}

N - Maximal Continuous Rest

一开始双指针写不知道哪错了,最后一个点过不了,看了题解因为是像一个环一样,所以我们可以数组再复制一遍到数组尾巴后面,这样后面和前面的1就接上了,而这样其实并不会改变原来其他连续1序列的长度,再扫一遍就好。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e5+10,INF=1e8;
int a[N*2];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    for (int i = 1; i <= n;i++)
    {
        cin >> a[i];
        a[i + n] = a[i];
    }
    ll ans = 0,sum=0;
    for (int i = 1; i <= 2*n;i++)
    {
        if(a[i]==1)
            sum++;
        else
        {
            ans = max(sum, ans);
            sum = 0;
        }
    }
    cout << ans;
    return 0;
}
posted @ 2022-03-18 12:04  menitrust  阅读(45)  评论(0)    收藏  举报