题目链接
翻译
给你一个初始数字x, 你可以对它做两种操作:
1.整除2然后加上10
2.减去10
问你在n次1操作和m次2操作之内, 能不能把数字x变成是小于0的。
题解
n和m都小于30?
这不傻逼题吗?
dp[i][j]表示i次1操作,j次1操作x能到达的最小值。
(dp[i][j] = min(dp[i-1][j]/2+10,dp[i][j-1] - 10))
最后康康 dp 数组里有没有小于等于0的数字就行了。
另解 : 会发现x / 2 + 10
这个操作 肯定不能 放在x - 10
操作之后的, 假设你这么做了, 那么两个操作之后, x
就变成(x - 10) / 2 + 10
也即x/2 + 5
,但是如果你先做x / 2 + 10
的话, 两个操作之后, x就会变成x/2
, 显然
更优秀, 所以你正确的做法应该是, 先让x一直做x/2 + 10
这个操作, 然后再一直做x-10
这个操作。
理解:如果有"21"这样的情况, 交换成"12"肯定更好。所以最后就变成"111122222"这样的操作了。
代码
#include<bits/stdc++.h>
#define ll long long
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%I64d",&x)
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
using namespace std;
const int N = 30;
int T,x,n,m;
int dp[N+5][N+50];
int main(){
#ifdef LOCAL_DEFINE
freopen("D:\rush.txt","r",stdin);
#endif
ios::sync_with_stdio(0),cin.tie(0);
rei(T);
while(T--){
rei(x);rei(n);rei(m);
rep1(i,0,N)
rep1(j,0,N)
dp[i][j] = x+1;
rep1(i,0,n)
rep1(j,0,m)
if (i==0 && j == 0){
dp[i][j] = x;
}else{
if (i>0){
dp[i][j] = min(dp[i][j],dp[i-1][j]/2 + 10);
}
if (j>0){
dp[i][j] = min(dp[i][j],dp[i][j-1] - 10);
}
}
bool ok = false;
rep1(i,0,n)
rep1(j,0,m)
if (dp[i][j]<=0){
ok = true;
break;
}
if (ok){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
}
return 0;
}