2021年省A第一场
A、卡片
0到9的卡片各2021张,够从1拼到哪个数字?
3181:用数组存储个数+遍历
int main(){
int have[10],now=1,x;
for (int i=0;i<10;i++) have[i]=2021;
while (1){
x=now;
while (x){//能否拼成
if (have[x%10])have[x%10]-=1;
else break;
x/=10;
}
if (x) break;
else now+=1;
}
cout<<now-1; //最后一个没拼成
return 0;
}
B、直线
40257:斜率为0和斜率不存在的情况共20+21=41条。对正常斜率的点计算k 和 b,因为doubl有精度损失千万不能用k算b和用b算k,都直接根据联立等式算!然后用map去重记录个数即可。
#define maxn 1000
#define maxx 20
#define maxy 21
using namespace std;
struct node{int x,y;};//存储点
map<double,map<double,int> > k; //储存斜率和截距
int main(){
int c=0,cnt=0,t;
node dot[maxn];
for (int i=0;i<maxy;i++){
for (int j=0;j<maxx;j++) dot[c++]={j,i};//记录点
}
for (int i=0;i<c;i++){//较小点
for (int j=i+1;j<c;j++){
if (dot[i].x!=dot[j].x && dot[i].y!=dot[j].y){//有斜率
double x = double(dot[j].y-dot[i].y)/double(dot[j].x-dot[i].x);
double b = double(dot[j].x*dot[i].y-dot[i].x*dot[j].y)/double(dot[j].x-dot[i].x);
if (k[x].find(b)==k[x].end()) cnt+=1;
k[x][b]=1;
}
}
}
cout<<cnt+maxx+maxy;
return 0;
}
C、货物摆放
2430:使得三个因子非降序,求其全排列之和
typedef long long ll;
int main(){
ll in = 2021041820210418,t,cnt=0;
//非降序三个因子
for (ll i=1;i<ll(sqrt(in))+1;i++){
if (in%i==0){
t = in/i;
for (ll j=i;j<ll(sqrt(t))+1;j++){
if (t%j==0){
if (i==j){//两个数字一样
if (i==t/j) cnt+=1;//三个数字一样
else cnt+=3;
}
else if (j==t/j) cnt+=3;//两个数字一样
else cnt+=6;//三个数字不同
}
}
}
}
cout<<cnt;
return 0;
}
D、路径
10266837:动态规划令dp存储长度,dp[1]=0,dp[x]=min(dp[可到达x的i]+i x之间的花费)
int gcd(int x,int y){return y?gcd(y,x%y):x;};
int main(){
int dp[2022],maxi;
dp[1]=0;
for (int i=2;i<2022;i++){
maxi = 99999999;
for (int j=max(1,i-21);j<i;j++){
if (dp[j]+i*j/gcd(i,j)<maxi) maxi=dp[j]+i*j/gcd(i,j); //找最短路径
}
dp[i]=maxi;
}
cout<<dp[2021];
return 0;
}
E、回路计数
881012367360:一开始想的是图+dfs,用cost[i][j]记录路径,2 ^ 21复杂度根本跑不完TAT。所以学习了状压dp,用21位二进制串表示第k栋有没有走过,以后就是哈密顿回路板子题:换成0到20号楼容易理解和计算。
typedef long long ll;
ll cost[25][25],dp[1<<21][25],res=0;
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;};
int main(){
//路径初始化
for (int i=1;i<22;i++){
for (int j=1;j<i;j++){
if (gcd(i,j)==1) cost[i-1][j-1]=cost[j-1][i-1]=1;//互质
else cost[i-1][j-1]=cost[j-1][i-1]=0;
}
cost[i-1][i-1]=0;
}
//状态压缩dp,dp[i][j]表示从i到j的方案数
dp[1][0]=1;
for (int i=1;i<(1<<21);i++){
for (int j=0;j<21;j++){
if (i>>j&1){//当前状态走过j
for (int k=0;k<21;k++){
if (!(i>>k&1) && cost[j][k])//未走过k,j k有路
dp[i+(1<<k)][k]+=dp[i][j];
}
}
}
}
//将所有可回到1的回路求和
for (int i=1;i<22;i++) res+=dp[(1<<21)-1][i];
cout<<res;
return 0;
}
2021年省A第二场
A、双乘积
59375:边遍历边求模
int main(){
int res = 1 , mod=100000;
for (int i=1;i<2022;i+=2) res = (res*i)%mod;
cout<<res;
return 0;
}
B、格点
15698:边遍历边判断
int main(){
int cnt=0;
for (int i=1;i<2022;i++){
for (int j=1;j<2022;j++){
if (i*j<=2021) cnt+=1;
}
}
cout<<cnt;
return 0;
}
C、整数分解
691677274345:乘法分解和加法分解我都形成非递减序列,分情况讨论暴力。但加法分解可套用隔板法模型,共2020个位置放置4个板C(20,4),所以小心long long溢出边乘边除几行代码就搞定 。。。
typedef long long ll;
int main(){
ll cnt=0;
for (int i=1;i<2021;i++){
for (int j=i;j<2021-i;j++){
for (int k=j;k<2021-i-j;k++){
for (int p=k;p<2021-i-j-k;p++){
int q = 2021-i-j-k-p;
if (q>=p){//非递减5个数序列
if (i==j){
if (j==k){
if (k==p){
if (p==q) cnt+=1; //C55 五个数相同
else cnt+=5;//C51四个数相同
}
else{
if (p==q) cnt+=10; //C52 3个数相同,2个数相同
else cnt+=20; //A52 3个数相同,2个数不同
}
}
else{
if (k==p){
if (p==q) cnt+=10; //C52 2个数相同,3个数相同
else cnt+=30;//C51*C42 2个数相同,2个数相同
}
else{//2个数相同
if (p==q) cnt+=30; //C51*C42 2个数相同
else cnt+=60; //A53 3个数不同
}
}
}
else{
if (j==k){
if (k==p){
if (p==q) cnt+=5; //C51 4个数相同
else cnt+=20;//A52 3个数相同,2个数不同
}
else{
if (p==q) cnt+=30;//C51*C42 2个数相同,2个数相同
else cnt+=60; //A53 3个数不同
}
}
else{
if (k==p){
if (p==q) cnt+=20; //A52 3个数相同,2个数不同
else cnt+=60; //A53 3个数不同
}
else{
if (p==q) cnt+=60; //A53 3个数不同
else cnt+=120; //A55 5个数不同
}
}
}
}
else break;//后面更不可能
}
}
}
}
cout<<cnt;
return 0;
}
隔板法,加法分解yyds
typedef long long ll;
int main(){
ll cnt=1;
for (ll i=1;i<5;i++){
cnt = (cnt*(2021-i))/i;//及时除以防止溢出
}
cout<<cnt;
return 0;
}
D、城邦
50468:2021个城市要连2020座桥,除了两头的都要加两次,所以找出耗费最多及第二多的点(使用数字1 7 8 9的)只加1次即可。
#define M 9999999
using namespace std;
typedef long long ll;
int sum(int x){
int res=0;
map<int,int> m;
for (int i=0;i<4;i++){
if (m.find(x%10)==m.end()){
m[x%10]=1;
res+=x%10;
}
x/=10;
}
return res;
}
int main(){
int max1=0,max2=0,r=0,t;
for (int i=1;i<2022;i++){
t = sum(i);
if (t>max1) max1=t;//最大
else if (t>max2) max2=t;//第二大
r += t;
}
cout<<r*2-max1-max2; //最费的当两头
return 0;
}
E、游戏
用动态规划做的但存不下长度为20210509的dp啊TAT, 还是并查集?