2034: Column Addition
2034: Column Addition
题目大意:给出一个加法竖式,和一个错误答案,去掉若干列让竖式成立,例如:
12127 117
+ 45618 = > + 468
———– ———
51825 585
那么输出需要去掉的列数的最小数量。
这道题比赛的时候很早就有思路了,用DP思想,设DP[i]为当前列不删除的情况下,需要删除的最少列数。因为状态由低位向高位转移,所以删除的一定是低位列。每一个不删除的列的状态来自于前一列或者是最近的没有删除的列,这些列的进位使得当前列计算正确,那么状态就得到保持,如果当前列一定要删除,那么DP[i] = -1;
状态转移方程:
dp[i]=min(dp[i],dp[j]+j−i−1),{j=i+1,..,n|a[i]+b[i]+jw[j]%10}
d
p
[
i
]
=
m
i
n
(
d
p
[
i
]
,
d
p
[
j
]
+
j
−
i
−
1
)
,
{
j
=
i
+
1
,
.
.
,
n
|
a
[
i
]
+
b
[
i
]
+
j
w
[
j
]
%
10
}
这里的a,b数组是对应的加数,jw数组是第i位的和的进位情况取值为1或者0。
需要注意的是最高位情况需要单独判断,因为它不可以有进位的情况。有则一定要删除最高位。
比赛还是因为心急没有过去,仅仅因为公式写错了一点。下不为例。
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<string>
#include<stdlib.h>
using namespace std;
const int maxn = 1000+5;
char cha[maxn],chc[maxn],chb[maxn];
int a[maxn],b[maxn],c[maxn];
int dp[maxn],jw[maxn];
int main()
{
//freopen("h.in","r",stdin);
//freopen("out.txt","w",stdout);
int n;
while(scanf("%d",&n)==1 && n )
{
for(int i=0;i<n;i++) dp[i]==-1;
scanf("%s",cha);
scanf("%s",chb);
scanf("%s",chc);
for(int i=0; i<n; i++)
{
a[i] = cha[i]-'0';
b[i] = chb[i]-'0';
c[i] = chc[i]-'0';
}
dp[n-1] = ((a[n-1]+b[n-1])%10==c[n-1]?0:-1);
if((a[n-1]+b[n-1])%10==c[n-1])
jw[n-1] = (a[n-1]+b[n-1]>9?1:0);
for(int i=n-2; i>0; i--)
{
dp[i] = ((a[i]+b[i])%10==c[i]?n-i-1:-1);
if((a[i]+b[i])%10==c[i])
jw[i] = (a[i]+b[i]>9?1:0);
for(int j=i+1; j<n; j++)
{
if((a[i]+b[i]+jw[j])%10 == c[i]&&dp[j]!=-1)
{
if(dp[i]!=-1)
dp[i] = min(dp[i],dp[j]+j-i-1);
else
dp[i] = dp[j]+j-i-1;
jw[i] = (a[i]+b[i]+jw[j]>9?1:0);
}
}
}
dp[0] = ((a[0]+b[0])==c[0]?n-1:-1);
for(int j=1;j<n;j++) {
if((a[0]+b[0])+jw[j] == c[0] &&dp[j]!=-1){
if(dp[0]!=-1)
dp[0] = min(dp[0],dp[j]+j-1);
else
dp[0] = dp[j]+j-1;
}
}
int minn = n;
for(int i=0;i<n;i++) {
if(dp[i]+i<minn &&dp[i]!=-1 && !jw[i])
minn = dp[i]+i;
}
printf("%d\n",minn);
}
return 0;
}