比赛链接:https://ac.nowcoder.com/acm/contest/5278/
H.纸牌游戏
题意:有n张牌,每张牌上有数字0-9,从中选m张牌,构成一个能被3整除的数,如果可以,输出最大值,否则输出-1。
解题思路:一个数能被3整除等价于数位之和能被3整除,既然要找最大的,最高位自然要选尽可能大的数字,当然你选的数量可能导致最后结果不满足条件,所以加选 max-1,max-2,至少有一种情况能满足条件(前提是本身有解),所以我们从高位dfs一下,然后取最大值即可。
还有一种方法,从高位选数,判断之后能否有解,主要注意check函数的写法。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(register int i=a;i<=b;++i)
#define drep(i,a,b) for(register int i=a;i>=b;--i)
const int maxn=1e5+5;
char s[maxn],ans[maxn];
int cnt[10],c[3];
int T;
inline bool check(int n,int t)//判断这位选完后还能否成立
{
rep(i,0,2)
c[i]=0;
rep(i,0,9)
c[i%3]+=cnt[i];
int down=max(0,n-c[1]-c[2]);//i下限
int up=min(n,c[0]);//i上限
drep(i,up,max(down,up-4))//枚举最大的3个i
{
int y=2*n-2*i-t;
y=(y%3+3)%3;//需要达到的数
int l=max(0,n-i-c[2]);//y的下限
int r=min(c[1],n-i);//y的上限
while(l%3!=y)
l++;
if(l<=r)
return 1;
}
return 0;
}
int main()
{
int n,m;
scanf("%d",&T);
while(T--)
{
scanf("%s",s+1);
n=strlen(s+1);
scanf("%d",&m);
rep(i,0,9)
cnt[i]=0;
rep(i,1,n)
cnt[s[i]-'0']++;
bool flag=1;//标记方案是否成立
int c=0;//余数
rep(i,1,m)//从高位向低位找
{
ans[i]=0;
drep(j,9,0)//从 9 向 0 找
if(cnt[j])
{
cnt[j]--;
int t=3-c-j;
t=(t%3+3)%3;
bool tag=check(m-i,t);
if(tag)
{
ans[i]=j+'0';//更新答案
c=(c+j)%3;//更新余数
break;
}
cnt[j]++;//这位取j不满足要把之前减的加回去
}
if(!ans[i]||(i==1&&m>1&&ans[1]=='0'))//注意不能有前导零
{
flag=0;
break;
}
}
ans[m+1]=0;
puts(!flag?"-1":ans+1);
}
return 0;
}