题意:n件物品切d刀分成d+1份,每份的和四舍五入,问总和最小。
思路:相当于有n-1个空,插d个板子,那么我们就可以DP解决他,dp[i][j]表示前i个空插了j个板子后的最小值
dp[i][j]表示前i个切了j刀得到的最小和(刀切在i后面或不切)。
转移: dp[i][j] = min(dp[i-1][j]+a[i],cal(dp[i-1][j-1]+a[i]));
代码一:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 const int maxn = 2e3+10; 7 int a[maxn],dp[maxn][25]; 8 9 int cal(int x){ 10 int yu = x%10; 11 if(yu>=5) x = x+10-yu; 12 else x = x/10*10; 13 return x; 14 } 15 16 int main(){ 17 int n,d; 18 while(scanf("%d%d",&n,&d)!=EOF){ 19 for(int i=1; i<=n; i++) 20 cin >> a[i]; 21 22 memset(dp,0x3f,sizeof(dp)); 23 dp[0][0] = 0; 24 for(int i=1; i<n; i++){ 25 for(int j=0; j<=d; j++){ 26 if(j==0) 27 dp[i][j] = dp[i-1][j]+a[i]; 28 else 29 dp[i][j] = min(dp[i-1][j]+a[i],cal(dp[i-1][j-1]+a[i])); // 前i-1个空用j个隔板,直接加,不四舍五入,如果四舍五入了,加下一个数的时候就不对了 30 // 前i-1个空用j-1个隔板,第j个隔板放在第i个数的后面,四舍五入。 31 } 32 } 33 int ans = 0x3f3f3f3f; 34 for(int i=0; i<=d; i++) 35 ans = min(cal(dp[n-1][i]+a[n]),ans); 36 37 cout << ans << endl; 38 } 39 }
代码二: d[i][k]表示把1~i份分成k份。dp[i][k] = min(dp[i][k],dp[j][k-1]+cal(sum[i]-sum[j])); j+1~i是一份
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 const int maxn = 2e3+10; 7 int a[maxn],sum[maxn],dp[maxn][25]; 8 9 int cal(int x){ 10 int yu = x%10; 11 if(yu>=5) x = x+10-yu; 12 else x = x/10*10; 13 return x; 14 } 15 16 int main(){ 17 int n,d; 18 while(scanf("%d%d",&n,&d)!=EOF){ 19 memset(sum,0,sizeof(sum)); 20 for(int i=1; i<=n; i++){ 21 cin >> a[i]; 22 sum[i] = sum[i-1]+a[i]; 23 } 24 25 memset(dp,0x3f,sizeof(dp)); 26 dp[0][0] = 0; 27 for(int i=1; i<=n; i++){ 28 dp[i][0] = cal(sum[i]); 29 for(int j=1; j<i; j++){ 30 for(int k=0; k<=d; k++) 31 dp[i][k] = min(dp[i][k],dp[j][k-1]+cal(sum[i]-sum[j])); 32 } 33 } 34 int ans = 0x3f3f3f3f; 35 for(int i=0; i<=d; i++) 36 ans = min(dp[n][i],ans); 37 38 cout << ans << endl; 39 } 40 }