iwtgm-20

题目链接
dp
确实没想到这种递推方式,一直绕在把整个网格分成k块,又要满足颜色不同,实在解不出来
dp的设置状态不是没想过,像这样的设置的确超出我的水平了

现在详细讲讲
只有两行,若两行的颜色块状态已知,我们是可以判断什么情况联通块会+1,什么情况是不变的,我们进行枚举即可
f[i][j][type]表示第i列,已经有j个联通块,type是上下颜色块的状态
这里我们设置0为BW,1为BB,2为WW,3为WB
来个例子:
第一列是B 第二列是B 那么我们的联通块数是不变的
W。 W 因为B与前一列的B属于同一个联通块,W同理

第一列是B 第二列是W 那么我们的联通块数+2
W。 B。 因为W不能属于前面任何一个联通块,联通块数+1,B同理

得到状态转移方程:
f[i][j][0]=f[i-1][j][0]+f[i-1][j-1][1]+f[i-1][j-1][2]+f[i-1][j-2][3];
f[i][j][1]=f[i-1][j][0]+f[i-1][j][1]+f[i-1][j-1][2]+f[i-1][j][3];
f[i][j][2]=f[i-1][j][0]+f[i-1][j-1][1]+f[i-1][j][2]+f[i-1][j][3];
f[i][j][3]=f[i-1][j-2][0]+f[i-1][j-1][1]+f[i-1][j-1][2]+f[i-1][j][3];

注意初始化第一列,第一列4种摆放方式都有可能

int n,k;
int dp[1010][2010][5];
const int mod=998244353;
void solve() {
    cin>>n>>k;
    dp[1][1][1]=1;dp[1][1][2]=1;dp[1][2][0]=1;dp[1][2][3]=1;
    for(int i=2;i<=n;i++){
        for(int j=1;j<=k;j++){
            dp[i][j][0]=(dp[i][j][0]+dp[i-1][j][0])%mod;dp[i][j][0]=(dp[i][j][0]+dp[i-1][j-1][1])%mod;dp[i][j][0]=(dp[i][j][0]+dp[i-1][j-1][2])%mod;if(j>=2)dp[i][j][0]=(dp[i][j][0]+dp[i-1][j-2][3])%mod;
            dp[i][j][1]=(dp[i][j][1]+dp[i-1][j][0])%mod;dp[i][j][1]=(dp[i][j][1]+dp[i-1][j][1])%mod;dp[i][j][1]=(dp[i][j][1]+dp[i-1][j-1][2])%mod;dp[i][j][1]=(dp[i][j][1]+dp[i-1][j][3])%mod;
            dp[i][j][2]=(dp[i][j][2]+dp[i-1][j][0])%mod;dp[i][j][2]=(dp[i][j][2]+dp[i-1][j-1][1])%mod;dp[i][j][2]=(dp[i][j][2]+dp[i-1][j][2])%mod;dp[i][j][2]=(dp[i][j][2]+dp[i-1][j][3])%mod;
            if(j>=2)dp[i][j][3]=(dp[i][j][3]+dp[i-1][j-2][0])%mod;dp[i][j][3]=(dp[i][j][3]+dp[i-1][j-1][1])%mod;dp[i][j][3]=(dp[i][j][3]+dp[i-1][j-1][2])%mod;dp[i][j][3]=(dp[i][j][3]+dp[i-1][j][3])%mod;
        }
    }
    ll ans=0;
    ans=(ans+dp[n][k][0])%mod;
    ans=(ans+dp[n][k][1])%mod;
    ans=(ans+dp[n][k][2])%mod;
    ans=(ans+dp[n][k][3])%mod;
    cout<<ans;
}

B.

又是一道从一开始就想错的题
因为后来才发现比如先往右上再往右下等价于往右移动两格,也就是用对角的方式走了水平或垂直的路,然后就去想把水平或竖直的路径变成对角的,认为直接走真正对角的不会最优
没想到就是先走真正的对角,然后再考虑把水平或竖直变成对角更为好做,服了

这个图中,我们可以把对角换成一个水平和一个竖直,也可以把一个水平和一个竖直换成一个对角

从原点到目的地需要的最小步数是max(a,b) (连这个都忘了我服了)
若k<max(a,b),那么就输出-1
考虑合法情况的最优解:
先走真正的对角到达b限定的高度
a和b表示终点坐标,设a为较大数

如果(a-b)%2==0,那么我们把用对角的方式走水平线(方式为必须两个水平线换两个对角)

第一次走到终点后,若剩余步数是偶数,那么用对角的方式从终点来回跳
若剩余步数是奇数,要求最后要走到目的地,那么就把前面的一个对角换成一条水平线和一条竖直线,此时对角的贡献-1,可以到达目的地

如果(a-b)%2==1,
那么必须要消耗一个步数走平路以到达目的地

第一次走到终点后,若剩余步数是偶数,那么以对角的方式在终点来回跳
若是奇数,把平路的那条改成对角,再花费一个步数从对角走到终点,对角+1

代码:

void solve() {
    ll n,m,k;cin>>n>>m>>k;
    if(n>m)swap(n,m);//n为较小数
    ll res=max(n,m);//从原点到终点所需最小步数
    if(res>k){//步数不够
        cout<<-1<<endl;return ;
    }
    ll ans=n;//先把真正的对角走完,此时到达b限定的高度
    ll x=k-res;//剩余的步数
    ll y=abs(n-m);//多出来的那段路
    if(y&1)ans+=y-1;//花费1个步数走平路,其余都走对角(是偶数)
    else ans+=y;//是偶数,全走对角
    if(x&1)ans+=x-1;//先把偶数个来回跳,剩下一个后面处理
    else ans+=x;//偶数直接来回跳完
    if(x&1&&y&1)ans++;//把平路花费的那一条变成对角,多出来的这一步从对角走到终点
    else if(y%2==0&&x&1)ans--;//把原来的一个对角改成平路,再加上剩余的1个也走平路,等价于原来的那个对角
    cout<<ans<<endl;
}

C.

选择每个序列的最后一个值,一定是最大的,如果这个组合没有被禁止,那么就输出
如果禁止了
答案的形式一定是某一个被禁止的组合调整一个取值所得到的组合,调整指将一个取值替换为前一个数
因为最大的组合被禁止了,我们此时应该去找这个组合调整一个取值所得到的组合,如果没有被禁止,就输出,如果又都被禁止了,那就只能从这些被禁止的组合里再去调整取值,然后因为我们是取最大值,所以多找是没有影响的
又知一个组合被调整一次一定优于调整两次的,也许你会说调整的那两个比那一个小,那么我会说那我直接调整那一个比较小的不就行了,一个小一定比两个小小
证毕
用到哈希,题解的代码写得还是非常优美,值得学习

const int b1=1e5+1,b2=3e5+3,m1=20060527,m2=21071179;
int n,m,cnt=1,num;
int tmp[11],a[11][N],c[N],ban[N>>1][11];
map<pair<int,int>,bool>mp;
struct build{
    int ans,p[11];
    pair<int,int>hsh;
}b[N*5];
void inw(){
    int sum=0,h1=0,h2=0;
    num++;
    for(int i=1;i<=n;i++){
        sum+=a[i][tmp[i]];
        b[num].p[i]=tmp[i];
        h1=(h1*b1%m1+tmp[i])%m1;
        h2=(h2*b2%m2+tmp[i])%m2;
    }
    b[num].ans=sum;
    b[num].hsh= make_pair(h1,h2);
}
bool cmp(build x,build y){
    return x.ans>y.ans;
}
void solve() {
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>tmp[i];c[i]=tmp[i];
        for(int j=1;j<=c[i];j++){
            cin>>a[i][j];
        }
    }
    inw();
    cin>>m;
    for(int i=1;i<=m;i++){
        int h1=0,h2=0;
        for(int j=1;j<=n;j++){
            cin>>ban[i][j];
            tmp[j]=ban[i][j];
            h1=(h1*b1%m1+tmp[j])%m1;
            h2=(h2*b2%m2+tmp[j])%m2;
        }
        mp[make_pair(h1,h2)]=1;
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++)tmp[j]=ban[i][j];
        for(int j=1;j<=n;j++){
            if(ban[i][j]==1)continue;
            tmp[j]--;
            inw();
            tmp[j]++;
        }
    }
    sort(b+1,b+1+num,cmp);
    for(int i=1;i<=num;i++){
        if(!mp[b[i].hsh]){
            for(int j=1;j<=n;j++){
                cout<<b[i].p[j]<<' ';
            }
            return ;
        }
    }
}
posted @ 2023-11-12 22:44  WW爆米花  阅读(4)  评论(0编辑  收藏  举报