ACM-ICPC 2018 焦作赛区网络预赛 B L. K

版权声明:本博客内容基本为原创,如有问题欢迎联系,转载请注明出处 https://blog.csdn.net/qq_41955236/article/details/82716539

先是B题,https://nanti.jisuanke.com/t/31711

题意:

       王子要破除m条魔咒冲出城堡,城堡一共有n个房间,每个房间都有一个权值,最初他手上有权值K,他要从第一个房间开始走到第n个房间,每次他可以选择是否破除这个魔咒,如果他要破除这个魔咒,他会得到的权值是x op[j] a[i]  ,x为当前的权值,op[j]是当前马上要破除的魔咒,a[i]是房间权值,魔咒是+-*/四种操作之一,他要出城堡,必须要破除所有的魔咒,魔咒的打破必须按顺序,房间也必须按顺序走,要你求一个最后能得到的最大的权值。

做法:

       在队友的建议下,我用了两个dp,一个存储当前的最小值,一个存储最大值,如果碰到负的a[i],那么就用最小值去更新最大值,用最大值去更新最小值,因为可能会碰到出现两个负数可以相互抵消的,所以需要两个dp来维护。


代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=(int)1e9+7;
const int maxn=1005;
ll a[maxn],dpbig[maxn][10],dpsmall[maxn][10];
char op[maxn];
int n,m,k;
ll deal(ll x,char op,ll y){
    if(op=='+') return x+y;
    if(op=='-') return x-y;
    if(op=='*') return x*y;
    if(op=='/') return x/y;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        scanf("%s",op+1);
        for(int i=0;i<=n;i++){
            for(int j=0;j<=m;j++){
                dpbig[i][j]=-1e18-5;
            }
        }
        for(int i=0;i<=n;i++){
            for(int j=0;j<=m;j++){
                dpsmall[i][j]=1e18+5;
            }
        }
        for(int i=0;i<=n;i++){
            dpbig[i][0]=dpsmall[i][0]=k;
        }

        for(int i=1;i<=n;i++){
            for(int j=1;j<=min(i,m);j++){
                if(a[i]>0||op[j]=='-'||op[j]=='+'){
                    ll now=deal(dpbig[i-1][j-1],op[j],a[i]);
                    dpbig[i][j]=max(dpbig[i][j],max(dpbig[i-1][j],now));
                    now=deal(dpsmall[i-1][j-1],op[j],a[i]);
                    dpsmall[i][j]=min(dpsmall[i][j],min(dpsmall[i-1][j],now));
                }
                else {
                    ll now=deal(dpbig[i-1][j-1],op[j],a[i]);
                    dpsmall[i][j]=min(dpsmall[i][j],min(dpsmall[i-1][j],now));
                    now=deal(dpsmall[i-1][j-1],op[j],a[i]);
                    dpbig[i][j]=max(dpbig[i][j],max(dpbig[i-1][j],now));
                }
            }
        }
        ll ans=-1e18-5;
        for(int i=1;i<=n;i++){
            ans=dpbig[i][m];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

L

题意:

       上帝是个吃货,现在他要吃n个小时的食物分别是鱼(f),肉(m),巧克力(c),如果连续三个小时吃同一种食物,他会不开心。有两种吃法会中毒,一是连续三个小时吃不同的食物,第二个小时吃了巧克力(即fcm和mcf);二是三个小时中在第一个和第三个小时都吃了巧克力的情况下,第二个小时吃了肉或者鱼(即cfc,cmc)。问这n个小时有多少种吃法会让他开心并且不中毒。

做法:

      第二个小时的时候是不会有不合法的情况的,我们会发现末尾结尾只有9种可能,cc,cm,cf,mf,mc,mm,fc,fm,ff。然后这些可能的后继状态又是可知的,即cc之后只能出现ccm和ccf,然后变出一个cm和cf,那么他们之间必然有联系。顺着这个思路想,可以找到递推关系。我将fm和mf分为一类(f(x)),将cc为一类(g(x)),将cm和cf分为一类(k(x)),把mm和ff分为一类(a(x)),再把mc和fc分为一类(z(x)),你会发现,每一类的后继状态一定是另一类下一状态的组成部分,比如刚刚枚举的cc,他变出cm和cf,所以g(x)*2就会是k(x+1)的组成部分之一。

       然后我们就可以得到矩阵啦;

      


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
struct matrix{
    ll aim[8][8];
    void clea(){
        memset(aim,0,sizeof(aim));
        for(int i=1;i<=5;i++)aim[i][i]=1;
    }
};
matrix mul(matrix a,matrix b){
    matrix ans;
    memset(ans.aim,0,sizeof(ans.aim));
    for(ll i=1;i<=5;i++){
        for(ll j=1;j<=5;j++){
            for(ll k=1;k<=5;k++){
                ans.aim[i][j]=(ans.aim[i][j]+a.aim[i][k]*b.aim[k][j])%mod;
            }
        }
    }
    return ans;
}
void pr(matrix a){
    for(int i=1;i<=5;i++){
        for(int j=1;j<=5;j++)
            printf("%d ",a.aim[i][j]);
        cout<<endl;
    }

}
matrix quick(matrix a,ll k){
    matrix ans;
    ans.clea();
    while(k){
        if(k&1){
            ans=mul(ans,a);
        }
        a=mul(a,a);
        k/=2;
    }
    return ans;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        ll n;
        scanf("%lld",&n);
        if(n==1){
            printf("3\n");
            continue;
        }
        if(n==2){
            printf("9\n");
            continue;
        }
        matrix a;
        memset(a.aim,0,sizeof(a.aim));

        a.aim[1][1]=a.aim[1][3]=a.aim[1][4]=a.aim[2][5]=a.aim[3][5]=1;
        a.aim[4][1]=a.aim[4][3]=a.aim[5][1]=a.aim[5][4]=1; a.aim[3][2]=2;
        a=quick(a,n-2);
        matrix b;
        memset(b.aim,0,sizeof(b.aim));
        b.aim[1][1]=b.aim[3][1]=b.aim[4][1]=2;
        b.aim[2][1]=1; b.aim[5][1]=2;
        a=mul(a,b);
        ll ans=0;
        for(int i=1;i<=6;i++)
            ans=(ans+a.aim[i][1])%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

最后来一个K题。

题意:

        给你n种船,每种船有两种参数,代表一共有2^{c_{i}}-1条载重量为v的船。现在有q个询问,问给你s重量的货物,你能找到多少种不同的载法(注意:每条船使用就必须满载,如果相同种类的船用的数量都一样那么算是同一种方法)。

做法:

       把数量用二进制展开,比如2的3次-1,就可以拆成1个2个和4个,就能够代表所有的容量了,这样的话最多只有20*20一共400个包,做一次01背包即可,非常方便的想法,队友在比赛的时候想到了,然而我完全没懂当时。。现在来补题了。。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=(int)1e9+7;
int dp[500][10005],wei[1005],n,q,num;
int main(){
    int t;
    cin>>t;
    while(t--){
        num=0;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++){
            int c,w;
            scanf("%d%d",&w,&c);
            int tmp=1;
            while(c--){
                wei[++num]=tmp*w;
                tmp*=2;
            }
        }
        dp[0][0]=1;
        for(int i=1;i<=num;i++){
            for(int j=0;j<=10000;j++){
                if(j<wei[i]) dp[i][j]=dp[i-1][j];
                else {
                    dp[i][j]=(dp[i-1][j]+dp[i-1][j-wei[i]])%mod;
                }
            }
        }
        while(q--){
            int aim;
            scanf("%d",&aim);
            printf("%d\n",dp[num][aim]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41955236/article/details/82716539