今天测试的三个题,除了第一个题全部爆炸
题目 A: 公交换乘
题目描述
【题目描述】
假设某条街上每一公里就有一个公共汽车站,并且乘车费用如下表:
公里数 1 2 3 4 5 6 7 8 9 10
费用 12 21 31 40 49 58 69 79 90 101
而任意一辆汽车从不行驶超过10公里。某人想恰好行驶n公里,假设他可以任意次换车,请你帮他找到一种乘车方案,使得总费用最小。
注意:10公里的费用比1公里小的情况是允许的。
【输入】
输入共两行,第一行为10个不超过200的整数,依次表示行驶1~10公里的费用,相邻两数间用一个空格隔开;第二行为某人想要行驶的公里数。
【输出】
输出仅一行,包含一个整数,表示行使这么远所需要的最小费用。
【输入样例】
12 21 31 40 49 58 69 79 90 101
15
【输出样例】
147
【数据规模】
100%的数据满足:输入中所有数均为正整数,且不超过200。
着个题很简单从前往后依次推就好
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int cost[11],n,dp[205]; 5 6 int main(){ 7 for(int i=1;i<=10;i++) scanf("%d",cost+i); 8 scanf("%d",&n); 9 memset(dp,0x3f,sizeof(dp));dp[0]=0; 10 for(int i=1;i<=n;i++){ 11 for(int j=i-1;j>=0&&i-j<=10;j--){ 12 dp[i]=min(dp[i],dp[j]+cost[i-j]); 13 } 14 } 15 printf("%d",dp[n]); 16 return 0; 17 }
题目 B: 数字矩形
题目描述
【题目描述】
给一个由数字组成的矩形,每次可以向左、向右、向下走,求一条从最上层的任意一个点走到最下层的任意一个点最小数字和的路径,输出其值。
【输入】
输入第一行包含两个数n, m,表示矩形的行数和列数。
接下来的n行每行有m个数,每个数表示矩形该位置上的值。
【输出】
输出最上层的任意一个点走到最下层的任意一个点的最小数字和的路径值。
【输出样例】
3 4
9 9 1 9
2 2 2 9
1 9 9 9
【输出样例】
8
【数据规模】
对于20%的数据,n, m不超过10
对于40%的数据,n * m不超过500
对于所有数据,n不超过100,m不超过500,每个数字不超过1000000。
这道题当时没反应过来,想用bfs做结果爆内存,0分。。
在讲过后才知道是个dp,用dp[i][j]表示从最上一层到当前节点的最优解,因为状态可由上方左方右方过来,所以可先枚举上方转移的,在枚举左方和右方,(右方和左方也行不影响)
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 5 int n,m; 6 7 ll dp[102][502],mp[102][502]; 8 9 ll min(ll x,ll y){ 10 return x>y?y:x; 11 } 12 13 int main(){ 14 scanf("%d%d",&n,&m); 15 for(int i=1;i<=n;i++) 16 for(int j=1;j<=m;j++){ 17 scanf("%d",&mp[i][j]); 18 } 19 memset(dp,0x3f,sizeof(dp)); 20 for(int i=1;i<=m;i++) dp[1][i]=mp[1][i]; 21 for(int i=2;i<=n;i++){ 22 for(int j=1;j<=m;j++) dp[i][j]=dp[i-1][j]+mp[i][j]; 23 for(int j=2;j<=m;j++) dp[i][j]=min(dp[i][j],dp[i][j-1]+mp[i][j]); 24 for(int j=m-1;j>=1;j--) dp[i][j]=min(dp[i][j],dp[i][j+1]+mp[i][j]); 25 } 26 ll ans=99999999999; 27 for(int i=1;i<=m;i++) ans=min(ans,dp[n][i]); 28 printf("%d",ans); 29 return 0; 30 }
题目 C: 排列
题目描述
【题目描述】
给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0)。例如123434有90种排列能被2整除,其中末位为2的有30种,末位为4的有60种。
【输入】
输入第一行是一个整数T,表示测试数据的个数,以下每行一组s和d,中间用空格隔开。s保证只包含数字0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
【输出】
每个数据仅一行,表示能被d整除的排列的个数。
【输入样例】
7
000 1
001 1
1234567890 1
123434 2
1234 7
12345 17
12345678 29
【输出样例】
1
3
3628800
90
3
6
1398
【样例解释】
在前三个例子中,排列分别有1, 3, 3628800种,它们都是1的倍数。
【数据规模】
20%的数据满足:s的长度不超过5, 1<=T<=5
50%的数据满足:s的长度不超过8
100%的数据满足:s的长度不超过12, 1<=d<=1000, 1<=T<=15,
一个状态压缩,用dp[i][j]表示i状态下除mod取余为j的方案数,则转移方程为dp[i|(1<<k)][j*10+s[k]-'0']+=dp[i][j];(假设k均插入最后一个)
还有一个细节是去重,可以直接先统计出每一个数字的个数,在最后除以个数的阶层
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int mod,dp[1<<12][1001],qc[]={0,0,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600},cnt[11]; 5 6 char s[14]; 7 8 int main(){ 9 int t;scanf("%d",&t); 10 while(t--){ 11 scanf("%s%d",&s,&mod); 12 int n=strlen(s); 13 int maxd=1<<n; 14 for(int i=0;i<maxd;i++) 15 for(int j=0;j<mod;j++) 16 dp[i][j]=0; 17 dp[0][0]=1; 18 for(int i=0;i<maxd;i++) 19 for(int j=0;j<mod;j++){ 20 for(int k=0;k<n;k++){ 21 if(!(i&(1<<k))) 22 dp[i|(1<<k)][(j*10+s[k]-'0')%mod]+=dp[i][j]; 23 } 24 } 25 memset(cnt,0,sizeof(cnt)); 26 for(int i=0;i<n;i++) cnt[s[i]-'0']++; 27 for(int i=0;i<=9;i++) if(cnt[i]>1) dp[maxd-1][0]/=qc[cnt[i]]; 28 printf("%d ",dp[maxd-1][0]); 29 } 30 return 0; 31 }