2020年“远光杯”粤澳计算机程序设计大赛网络资格赛 题解2

2020年“远光杯”粤澳计算机程序设计大赛网络资格赛 题解1

由于题目太多分成两篇文章总结,这一篇是后 11 至 20题。
加上代码头文章太长,所以把模板放在最后。

如果有什么讲的不清楚的欢迎留言私信交流~

文章目录

  • K . 项目管理
  • L . 捕鱼达人
  • M . 排除危险
  • N . 图像编码问题
  • O . 军训值日生
  • P . 今天图书馆开了没?
  • Q . 小明的体育课
  • R . 孤独的字符串
  • S . 鲍勃的输入法
  • T . 一起做课件
  • ends: 代码头

K . 项目管理

思路:贪心,把用时间长的安排在前面,这样在他在工作的时间可以去给其他人安排。

代码

struct node{
    ll x,y;
}a[manx];
bool cmp(node a,node b){
    return a.y>b.y;
}

int main(){
    ll n; cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i].x>>a[i].y;
    ll ans=0,cnt=0;
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++){
        cnt+=a[i].x;
        ans=max(ans,cnt+a[i].y);
    }
    cout<<"Project "<<n<<": "<<ans<<endl;
    return 0;
}

L . 捕鱼达人

思路

  1. 本来是想用LIS的,但是发现顺序不能改变,所以就没办法了。
  2. 考虑优化求该位置前的最大值,用线段树优化+ dp。
  3. 以魅力值的值域作为线段树的结点,dp数组维护以 i 结尾的序列的价值最大和。
  4. 遍历的时候把枚举的元素的价值加入到该魅力值的结点,在遍历的时候更新最大值,也就是取max,由于可以从左到右和从右到左,所以这个过程重复两次。

代码

const int manx=2e5+5;

ll yz[manx],val[manx];
ll dp[manx];
struct node{
    ll l,r,x;
}a[manx*5];
void build(ll k,ll l,ll r){
    a[k].l=l,a[k].r=r;
    if(l==r){
        a[k].x=0; return ;
    }
    ll mid= l+r>>1;
    build(kl,l,mid); build(kr,mid+1,r);
    a[k].x=max(a[kl].x,a[kr].x);
}
ll query(ll k,ll l,ll r){
    if(a[k].l>=l&&a[k].r<=r){
        return a[k].x;
    }
    ll mid= (a[k].l+a[k].r)>>1;
    ll ans=0;
    if(l<=mid) ans=max(ans,query(kl,l,r));
    if(r>mid) ans=max(ans,query(kr,l,r));
    return ans;
}
void update(ll k,ll pos,ll v){
    if(a[k].l==a[k].r){
        a[k].x=v;
        return ;
    }
    ll mid= (a[k].l+a[k].r)>>1;
    if(pos<=mid) update(kl,pos,v);
    else update(kr,pos,v);
    a[k].x=max(a[kl].x,a[kr].x);
}
int main(){
    io;
    ll p; cin>>p; ll cnt=0;
    while(p--){
        ll n,x,ans=0,dd=0; cin>>n;
        for(int i=1;i<=n;i++){
            cin>>x;
            yz[i]=x%10000;  yz[i]+=60001;
            val[i]=x/10000;
            if(yz[i]>dd) dd=yz[i];
        }
        build(1,1,dd);
        for(int i=1;i<=n;i++){
            ll mx=query(1,1,yz[i]);
            dp[i]=mx+val[i];
            ans=max(ans,dp[i]);
            update(1,yz[i],dp[i]);
        }
        build(1,1,dd);
        for(int i=n;i>=1;i--){
            ll mx=query(1,1,yz[i]);
            dp[i]=mx+val[i];
            ans=max(ans,dp[i]);
            update(1,yz[i],dp[i]);
        }
        cout<<"Case #"<<++cnt<<": ";
        cout<<ans<<endl;
    }
    return 0;
}

M . 排除危险

思路: 根据题意模拟就可以,让化学物品ans!=元素种类cnt。

代码

ll cnt;
map<ll,ll>vis;

int main(){
    ll x,y; ll ans=0,res=0,n=0;
    while(cin>>x){
        if(x==-1) break;
        cin>>y;
        n++;
        res=0;
        if(vis[x]==0) res++; vis[x]++;
        if(vis[y]==0) res++; vis[y]++;
        ans++; cnt+=res;
        if(ans==cnt){
            ans--; cnt-=res;
            vis[x]--; vis[y]--;
        }
     //   cout<
    }
    cout<<n-ans<<endl;
    return 0;
}

N . 图像编码问题

思路: 根据题意模拟,注意合并s1和s2之后要注意去掉前缀0 。

代码

struct node{
    ll x,val;
}a[manx];
ll k=0;
bool cmp(node a,node b){
    return a.val<b.val;
}


void pd(ll l,ll r){
    string sl="",sr="";
    do{
        if(l&1) sl+="1";
        else sl+="0";
        l/=2;
    }while(l);
     do{
        if(r&1) sr+="1";
        else sr+="0";
        r/=2;
    }while(r);
    while(sl.size()<sr.size()) sl+="0";
    while(sr.size()<sl.size()) sr+="0";
 //   cout<
    reverse(sl.begin(),sl.end());
    reverse(sr.begin(),sr.end());
    string s="";
    for(int i=0;i<sl.size();i++)
        s+=sl[i],s+=sr[i];
    ll p=0;
    for(int i=0;i<s.size();i++)
        if(s[i]=='0') p++;
        else break;
    s.erase(0,p);
    ll x=1,ans=0;
    if(s.size()==0){
         a[k].val=ans;
         return ;
    }
    for(int i=s.size()-1;i>=0;i--){
        if(s[i]=='1') ans+=x;
        x<<=1;
    }
    a[k].val=ans;
   // cout<
}

int main(){
    ll n; cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            cin>>a[++k].x;
            pd(i-1,j-1);
        }
    sort(a+1,a+1+k,cmp);
    for(int i=1;i<=k;i++){
        ll cnt=1,p=i+1;
        while(p<=k&&a[i].x==a[p].x) cnt++,p++;
        if(p!=k+1&&i!=k) cout<<a[i].x<<","<<cnt<<" ";
        else  cout<<a[i].x<<","<<cnt;
        i=i+cnt-1;
    }
    return 0;
}

O . 军训值日生

思路:区间dp,s维护1~i 的物品和,dp维护l~r的消耗,不会的可以去做一下合并石子。

代码

ll a[manx],s[manx]; ll dp[manx][manx];

int main(){
    ll n;
    while(cin>>n){
        ll ans=0;
        memset(dp,inf,sizeof(dp));
        s[0]=0;
        for(int i=1;i<=n;i++) cin>>a[i],dp[i][i]=0,s[i]=s[i-1]+a[i];
        for(int len=2;len<=n;len++){
            for(int i=1;i<=n;i++){
                int j=i+len-1;
                if(j>n) continue;
                for(int k=i;k<=j;k++){
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+(s[k]-s[i-1])*(s[j]-s[k]));
                }
            }
        }
        cout<<dp[1][n]<<endl;
    }
    return 0;
}

P . 今天图书馆开了没?

思路 d p [ i ] [ j ] dp[i][j] dp[i][j]维护第 i 天去第 j 个图书馆的最大值,只有两种情况:

  1. 当天有开图书馆且前一天该图书馆没开,有:
    d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ k ] + 1 ) dp[i][j]=max(dp[i][j],dp[i-1][k]+1) dp[i][j]=max(dp[i][j],dp[i1][k]+1)
  2. 其余情况都是:
    d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ k ] ) dp[i][j]=max(dp[i][j],dp[i-1][k]) dp[i][j]=max(dp[i][j],dp[i1][k])

代码

const int manx=100000+5;
ll a[manx][5],dp[manx][5];
int main(){
    io;
    ll n; cin>>n;
    for(int i=1;i<=n;i++){
       for(int j=1;j<=4;j++) cin>>a[i][j];
    }
    for(int j=1;j<=4;j++) dp[1][j]=a[1][j];
    for(int i=2;i<=n;i++){
        for(int j=1;j<=4;j++){
            if(a[i][j])
                for(int k=1;k<=4;k++){
                    if(j==k&&a[i-1][j]) dp[i][j]=max(dp[i][j],dp[i-1][j]);
                    else dp[i][j]=max(dp[i][j],dp[i-1][k]+1);
                }
            else  for(int k=1;k<=4;k++) dp[i][j]=max(dp[i][j],dp[i-1][k]);
        }
    }
    ll ans=0;
    for(int i=1;i<=4;i++)
        ans=max(ans,dp[n][i]);
    cout<<ans<<endl;
  //  cout<
    return 0;
}

Q . 小明的体育课

思路:经典爬楼梯~dp[i]维护到达i点的方案,只能走1/2步,所以由前两个转移。

代码

ll dp[50];

int main(){
	ll n;  io; dp[1]=1; dp[2]=2;
	for(int i=3;i<=35;i++) dp[i]=dp[i-1]+dp[i-2];
	//for(int i=3;i<=35;i++)  cout<
	while(cin>>n){
        cout<<dp[n]<<endl;
	}
	return 0;
}

R . 孤独的字符串

思路

  1. 用dp[i][j][1/0]维护前i个元素状态为j是否存在aba子串的方案数
    因为只有两个字符,所以把a看成0,b看成1
    状态只有4(2^2)个 aa为0 ab为1 ba为2 bb为3

  2. 递推过程:
    考虑8次 四个状态* (合法+不合法)
    例如
    00 可以由 0 00 1 00 推出
    01 可以由 1 01 0 01 推出
    就是在前面加上0和1两种
    但是要注意合法性
    比如说 dp[i][1][0]
    本来是dp[i][1][0]+=dp[i-1][0][0] + dp[i-1][2][0]
    注意 2的二进制位为10 恒定合法
    所以只能写成 dp[i][1][0]+=dp[i-1][0][0]

  3. 再比如说
    dp[i][0][0] += dp[i-1][0][0]+dp[i-1][2][0]
    这个的意思就是前i位的后两位 为 00 的状态且不合法的个数
    它由什么递推?肯定前一位是0 再前一位可以是0也可以是1
    就是 dp[i-1][0(00)][0]+dp[i-1][1(10)][0]

代码

t = int(input())
dp = [[[0 for i in range(3)] for i in range(5)] for i in range(110)]
dp[2][0][0] = 1  # aa
dp[2][1][1] = 1  # ab
dp[2][2][0] = 1  # ba
dp[2][3][0] = 1  # bb
for i in range(105):
    dp[i][0][0] += dp[i-1][0][0]+dp[i-1][2][0]
    dp[i][1][0] += dp[i-1][0][0]
    dp[i][2][0] += dp[i-1][1][0]+dp[i-1][3][0]
    dp[i][3][0] += dp[i-1][1][0]+dp[i-1][3][0]
    #
    dp[i][0][1] += dp[i-1][0][1]+dp[i-1][2][1]
    dp[i][1][1] += dp[i-1][0][1]+dp[i-1][2][1]+dp[i-1][2][0]
    dp[i][2][1] += dp[i-1][1][1]+dp[i-1][3][1]
    dp[i][3][1] += dp[i-1][1][1]+dp[i-1][3][1]
for i in range(t):
    x = int(input())
    ans = 0
    if(x == 1 or x == 2):
        print(x)
    else:
        for i in range(4):
            ans += dp[x][i][1]
        ans += dp[x][2][0]
        print(int(ans))

S . 鲍勃的输入法

思路:字典树+dfs 但是写挂了。 2020.4.26 订正:

  1. 字典树的查找过程其实就是按字典序顺序查找的,所以标记字符串的结点位置和每个结点对应的字符串,然后遍历输出即可,当输出达到50个就return 。

代码

const int manx=100000+5;
struct node{
    ll son[26],cnt;
    bool flag;
}a[800000];
ll id,cnt;
map<string,ll>vis;
map<ll,string>ans;

void add(string s){
    int n=s.size();
    ll u=0;
    for(int i=0;i<n;i++){
        char x=s[i];
        ll v= x-'a';
        if(!a[u].son[v]) a[u].son[v]=++id;
        u=a[u].son[v];
    }
    a[u].flag=true; vis[s]=u; ans[u]=s;
}
void dfs(ll u){
    if(cnt>=50) return ;
    if(a[u].flag) cout<<ans[u]<<endl,cnt++;
    for(int i=0;i<26;i++){
        ll v=a[u].son[i];
        if(v)
            dfs(v);
    }
}
ll query(string s){
    int n=s.size(); ll u=0;
    for(int i=0;i<n;i++){
        ll v=s[i]-'a';
        if(!a[u].son[v]) return -1;
        u=a[u].son[v];
    }
    return u;
}
int main(){
    ll n; cin>>n;
    for(int i=1;i<=n;i++){
        string s; cin>>s;
        if(!vis[s]) add(s);
    }
    ll m; cin>>m;
    while(m--){
        cnt=0;
        string s; cin>>s;
        if(!vis[s]) cout<<s<<endl,++cnt;
        ll f=query(s);
        if(f==-1) add(s);
        else dfs(f);
    }
    return 0;
}

T . 一起做课件

思路

代码

ends: 代码头

#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
inline ll read(){ll s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
void put1(){ puts("Yes") ;}void put2(){ puts("No") ;}void put3(){ puts("-1"); }
ll qp(ll a,ll b, ll p){ll ans = 1;while(b){if(b&1){ans = (ans*a)%p;--b;}a =
(a*a)%p;b >>= 1;}return ans%p;}
const int mo=998244353; const int mod=1000000007;

const int manx=1e6+5;
int main(){

    return 0;
}

你可能感兴趣的:(acm)