题解【CF1551D1】 记忆化搜索

题意: 有一个m*n的桌子,和 $ (m*n)/2 $ 块多米诺 每块多米诺是2*1的 ,问恰好有k块多米诺水平放置时能否放满整个桌面

做法:记忆化搜索

灵感:首先看到数据范围 n,m<=100 ,所以想到 $ O(nmk) $ 的算法,

于是,我发现,对于一个桌面,我们要么放一行,要么放一列。

状态 dp[i][j][k]表示放满能否用恰好k个垂直放满i*j的桌面

转移 如果目前一行的格子数是2的倍数而且平行的个数足够放一行,那么如果放了后的状态可行,该状态可行,如过一列的格子数是偶数,竖直的个数足够,那么同样放一列。

$$ dp[i][j][k] |= \begin{cases} dp[i][j-1][k] (i|2,i/2 \leq x*y/2-z ) \\dp[i-1][j][k-(j/2)] ( j|2,j/2\leq k) \end{cases} $$

边界 dp[0][0][0]=1;(不用放了)

dp[i][j][k]=1 , $ k=0,i%2=0 $ 直接铺满即可

dp[i][j][k]=1 , $ k=i*j,j%2=0 $ 同上

其他情况 dp[i][j][k] = 0 ( 不能由合法状态转移来的)

代码

#include<bits/stdc++.h>
#define int long long
#define rg register
#define __max(aaa,bbb,ccc) max(max(aaa,bbb),ccc)
#define __min(aaa,bbb,ccc) min(min(aaa,bbb),ccc)

using namespace std;
template <typename T> inline void read(T &x)
{
    x=0;int f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x*=f;
}
template<typename T,typename ...Args>void read(T &x,Args&...args){read(x),read(args...);}

template <typename T> inline void print(T x)
{
    if(x>9) print(x/10);
    putchar(x%10+48);
}
int n,m,t,k;
bool f[101][101][5001],vis[101][101][5001];
inline bool can(int x,int y,int z)
{ 
    if(vis[x][y][z])return f[x][y][z];
    vis[x][y][z]=1;
    if(z>(x*y)/2)return f[x][y][z]=false;
    if(x==0&&y==0&&z==0)return f[0][0][0]=true;
    if(z==0&&x%2==0)return f[x][y][z]=true;
    if(z==x*y/2&&y%2==0)return f[x][y][z]=true;
    bool ans=0;
    if(x%2==0&&(x/2)<=(x*y/2)-z) ans=(ans||can(x,y-1,z));
    if(y%2==0&&(y/2)<=z) ans=(ans||can(x-1,y,z-(y/2)));
    if(ans)return f[x][y][z]=true;
    return false;
}
signed  main()
{
       read(t);
       for(register int _=1;_<=t;++_)
         {
            read(n,m,k);
            if(can(n,m,k))puts("YES");
            else puts("NO"); 
         }
}
posted @ 2021-08-04 10:52  寂静的海底  阅读(0)  评论(0编辑  收藏  举报  来源