暑假test01小结

B题:

题意:2n个篮球矮人;每行n个共2行;每第一次可以选任意一行的一个,下一次就只能选另一行了,并且选了第i个选手,(0-i)就都不能选共n个嘛,所以此教练就是,看了第一排就一定会看第二排,反之同理,并且,眼睛只能从左到右瞄,不能反向偏头,(好辛苦的选人啊)

题目链接:https://cn.vjudge.net/contest/313272#problem/B

0代表第一行,1代表第二行

ps:此也是线性DP; 第i,0个人,就只能是(i-1,0)没有被选上,或者(i-1,1)被选上了或者此人没被选上,(i,1)同理;

故多开个【2】,0代表没被选上,[1]代表被选上;

#include <iostream>
#include<algorithm>
#include<stdio.h>
typedef long long int ll;
using namespace std;
const int N=100005;
ll a[N][2]; //此题身高为10^9,故用LONGLONG;
ll dp[N][2][2];//中间的[2]代表行数最后的[2]:0代表over,1代表get;
int main()
{
    int n;
    scanf("%d",&n); 
    for(int i=0;i<n;i++) //第一排
    {
        scanf("%lld",&a[i][0]);
    }
    for(int i=0;i<n;i++)//第二排
    {
        scanf("%lld",&a[i][1]);
    }

    //dp[0][0][0]=0;    dp[0][1][0]=0;
    dp[0][0][1]=a[0][0];dp[0][1][1]=a[0][1];  //初始化


    for(int i=1;i<n;i++)
    {
        dp[i][0][1] = max(max(dp[i-1][1][0], dp[i-1][1][1]),dp[i-1][0][0])+a[i][0];//第一排第i个人选上了
        dp[i][0][0] = max(dp[i-1][1][1], dp[i - 1][1][0]);                    //没被选上
        dp[i][1][1] = max(max(dp[i-1][0][0], dp[i-1][0][1]),dp[i-1][1][0])+ a[i][1];//第二排第i个人呗选上
        dp[i][1][0] = max(dp[i-1][0][1], dp[i-1][0][0]);//没被选上
    }
    printf("%lld\n",max(max(dp[n-1][0][0],dp[n-1][0][1]),max(dp[n-1][1][1],dp[n-1][1][0])));//从四个里面挑最大的给我上,冲鸭,我的最高身高篮球队
                                                                                            //话说我是一个只看身高的教练
     //cout<<<<endl;               
     return 0;
}

C题题目链接:http://codeforces.com/problemset/problem/1195/D1

此题给出一个数字n,代表n个数,a1--an;每个数字的长度一样,求f(ai,aj) i和j的范围都是1-n;f(x,y)就等于把相同位数的数字相互穿插在一起。此题由于限定每个数的长度一样,故每个数的第k位,在2k和2k-1各出现n次;所以可以直接123==112233*n;把每个数累加起来;

#include <iostream>
#include <algorithm>
#include <stack>
#include <stdio.h>
using namespace std;
typedef long long int ll;
const int N = 100005;
const int mod = 998244353;
int n;
ll a[N];
ll func(ll x)
{
    ll res=0;
    stack<int >st;
    while(x)
    {
        st.push(x%10);
        x/=10;
    }

    while(st.size())
    {
        res=res*10+st.top();
        res=res*10+st.top();
        st.pop();
        res%=mod;
    }
    return (res*n)%mod;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=(ans+func(a[i]))%mod;
        //ans%=mod;
    }

    printf("%lld\n",ans);
    return 0;
}

D题:也就是c题的升级版链接:http://codeforces.com/problemset/problem/1195/D2

此题和上题一样,但是少了个位数一样的限定,也就是说上一题n个数每个数的位数相同,此题却可以不同;暂时还没弄出来;

mark;

F题:http://codeforces.com/problemset/problem/1138/D

给以你一个串s,和串t,要使s串中字符个数不变,位置可变使子串t出现次数最多;

ps:可用next数组存下t串,主要作用为记录t串相同前缀,方便回溯;然后记录s串个数,按t串的顺序+到新的ans串中,当组合成一个t串后,嘿嘿,kmp的作用就来了,直接回到前面有重合的部分,也就是j=next[j],回溯,这样可以用最少的字符,组出更多的t;因为t串可能有前后相同部分;

#include <iostream>
#include <algorithm>
#include <stack>
#include <stdio.h>
#include<cstring>
#include<string.h>
using namespace std;
string s,t,str;
int   next2[500005];
void getnext2()     //构造t串next数组 
{
    int j=0,k=-1;
    next2[0]=-1;
    while(j<t.size()){
        if(k==-1||t[j]==t[k]){
            j++,k++;
            next2[j]=k;
        }
        else
            k=next2[k];
    }
}

int main()
{
    int len1,len2,ans=0;
    cin>>s>>t;
    len1=s.size();
    len2=t.size();
    getnext2();
    int cnt0=0,cnt1=0;
    for(int i=0;i<len1;i++){   //记录 s串 0和1的个数 
        if(s[i]=='0')cnt0++;
        else
            cnt1++;
    }
    if(len1<len2)cout<<s<<endl;//如果s串长度小于t串 直接结束;
	 
    else{
            int j=0;
        while(cnt0>0&&cnt1>0){
            if(j>=len2)j=next2[j];//每当构造出一个t串 回溯; 
            if(t[j]=='0'){
                cnt0--;
                str+='0';
            }
            if(t[j]=='1'){
                cnt1--;
                str+='1';
            }
            j++;
        }
        
        while(cnt0){//将剩余的字符补上 
            cnt0--;
            str+='0';
        }
         while(cnt1){
            cnt1--;
            str+='1';
        }
        cout<<str<<endl;
    }


    return 0;

}

G题题目链接:https://hihocoder.com/problemset/problem/1948

题意:输入一个N和一个K;N代表N个数,K代表分为K组;每组 组内相加 即每组的值,再从每组之间的值取最小值,即为答案;

此题:直接用二分法寻找答案,答案的上限为所有数相加,每次取中间值mid;从1-n累加,当大于mid时划为一组,当组数大于k组时很显然mid取小了,故left=mid+1,right不变;当取完所有数分不到k组这么多时,mid太大了,right=mid-1,left不变;最后找到一个mid值不上不下正吻合即为答案;

#include <iostream>
#include <algorithm>
#include <stack>
#include <stdio.h>
using namespace std;
#define ll long long int
const int N = 100005;
int a[N];
int l;
int n,k;
bool check(ll t)  //看mid大不大 
{
    ll re=0,cnt=0;
    for(int i=1;i<=n;i++)
    {
        re+=a[i];
        if(re>=t)
        {
            re=0;
            cnt++;
            if(cnt==k)return true;
        }
    }
    return false;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin>>n>>k;
    ll sum=0,ans;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    ll mid;

    while(l<=sum)   //二分寻找mid; 
    {
        mid=(l+sum)>>1;
        if(check(mid)){
            ans=mid;
            l=mid+1;
        }
        else{
            sum=mid-1;
        }
    }
    cout<<ans<<endl;


    return 0;
}

H题:https://hihocoder.com/problemset/problem/1934

此题虽然很简单,但是我死活没做出来,没想到,当然是我太菜了;

题意:刷油漆,给你每个木板的高度,每次只能刷一行(当然必须是连续的一行,断了后,刷的次数当然要+1);

ps:记一个数为ans,将a[1]加给ans,记第一次要刷a[1]次,如果a[i+1]>a[i]则多刷a[i+1]-a[i]次,如果小于,当然我第一次a[1]可以连续下去,你别我a[1]小,我当然可以把你刷完;

代码很简单就不上了(占空间);

I题:https://hihocoder.com/problemset/problem/1979

题意:一个人去旅行,N个城市M天;每个城市每一天有不同的天气:X代表雨天,O代表晴天,男主只能在有太阳的晴天旅游,不然go,die;可以从任意一天出发到第一个城市,(必须从第一个城市开始到第N个城市按顺序旅游,ps:男主有点执着)求最少可以在第几天旅游完N个城市;

此题明显是DP;首先构建一个地图maze[n][m];代表每个城市,第几天的天气;当第i个城市第J天可以去旅游的话,则第i-1个城市第j-1天我曾经旅游过,并且maze[i-1][j-1]一定是晴天,且今天一定是晴天,我才可以来也就是maze[i][j]=='O';也可能是i个城市第j-1天我旅游过,并且一定是晴天;

那么dp方程很简单了  ,我开个bool数组,代表该座城市,第j天我有没有呆的可能;

首先得初始化,将所有的第0个城市所有天赋1,代表我都曾经去过,也将所有第0个城市的天气变为晴天;也就是说,我可以从任意一天开始第一个城市的旅行;

   if(maze[i-1][j-1]=='O'&&dp[i-1][j-1]&&maze[i][j]=='O')dp[i][j]=1; 或者,maze[i][j-1]=='O'&&dp[i][j-1]==1&&maze[i][j]=='O';

接来下代码

#include <iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
using namespace std;
const int N=1005;
int n,m,flag;
int dp[N][N];
char maze[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)  //赋值 
    {
        maze[i][0]='O';
        //dp[i][0]=1;
        for(int j=1;j<=m;j++)
        {
            cin>>maze[i][j];
            maze[0][j]='O';
            dp[0][j]=1;
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(maze[i][j]=='O'&&maze[i][j-1]=='O'&&dp[i][j-1])
                dp[i][j]=1;
            if(maze[i][j]=='O'&&maze[i-1][j-1]=='O'&&dp[i-1][j-1])
                dp[i][j]=1;
        }
    }
    for(int j=1;j<=m;j++){
        if(dp[n][j]){
                flag=1;
            cout<<j<<endl;
            break;
        }
    }
    if(!flag)cout<<"-1"<<endl;
    
    return 0;
}

   J题:https://hihocoder.com/problemset/problem/1838

输入一个N,再输入2*N+1张牌,每张牌有数字A,B;你抽N+1张,要使你手上牌的A之和  和 B之和 大于剩下的A和,和B的和;

此题为贪心;怎么贪: 将A或者B排个序,分为两两一组,如果将A从小到大排序,那么从每组中选取B更大的一张;这么久保证了你手上的B一定大于剩下的了吧;然后:下一组的A一定大于前一组未选的牌的A,最后还剩最后一张最大的牌A,比最后一组的未选的牌A要大。

也就是说,你每组的B都比未选的B要大,最后还余下一张最后一张牌B;你每下一组的A都比前一组未选的A大,最后还余一张第一组的A,所以于情于理你手上的A,B都是最大的;

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int N = 3*100005;
int inde[N];
struct p
{
    int a,b;
    int pos;
}t[N];
bool cmp(p x,p y)
{
    return x.a<y.a;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=2*n+1;i++)  //输入,记录输入的顺序,防止s排序使将顺序打乱; 
    {
        cin>>t[i].a>>t[i].b;
        t[i].pos=i;
    }
    
    sort(t+1,t+1+2*n+1,cmp);
    int in=1;
    for(int i=1;i<=2*n;i+=2) //inde【】记录牌序; 
    {
        if(t[i].b<t[i+1].b)
            inde[in++]=t[i+1].pos;
        else
            inde[in++]=t[i].pos;
    }
    inde[in++]=t[2*n+1].pos;
    for(int i=1;i<in;i++) //输出 
        cout<<inde[i]<<endl;
    return 0;
}
发布了97 篇原创文章 · 获赞 3 · 访问量 9447

猜你喜欢

转载自blog.csdn.net/foolishpichao/article/details/97643854