这道题网上的二维DP标准算法是完全错误的,花了我好几天去思考,后来通过一点点模拟总算找到了错误,且听我慢慢道来,至于错误程序网上有很多,可以去搜索我就不给链接了。
还有,UVA323这道题加强过数据,这才能验证是否是正确的程序。
网上广泛流传的二维DP思路是已经已经选择多少人->此次选择哪个人->差值之和,因为不能重复,所以加入一个判断的过程,
9 6 6 2 16 10 4 9 19 8 17 12 4 7 10 2 2 14 5 18 0 0
这组数据的答案是
Jury #1
Best jury has value 54 for prosecution and value 54 for defence:
1 2 3 4 6 9
但是错误程序的答案是
Jury #1
Best jury has value 52 for prosecution and value 52 for defence:
1 3 4 5 6 8
warning:我后面讲的数是指一组数据的前面一个数减去后面一个数,加起来指的是这些数加起来,而和指的是一组数据中前一个加后一个。(这样是不是好难理解)这样说比较方便。如这组数据:数分别为{4, 6, -5, 11, 5, -3, 8, -12, -13};加起来指的是4+6或者4+-5这种。和分别是{8,26,13,27,29,11,12,16,23}。务必要看懂这句话。
通过正确的答案和程序,运行得到dp[5][-4+6*20] = 100其中6×20是相当于零点,而-4代表的是第一组数据,9和6,下一个只要是4就可以得出正确的结论,即108。
这个数据是给错误的程序做准备的,因为它不是一个一个枚举,它是通过一个一个去试,然后通过上一个状态寻找该状态该点的最大值。
在错误程序测试dp[5][-4+6*20]也是得到100,这让我赶脚很不对啊,如果是这样的话,答案怎么可能是52+52=104啊,明显应该108啊。
大牛们肯定已经想到了,一定是因为那个所谓顺藤摸瓜的函数,那5个数之间可能就有第一个数,所以不能选第一个数,所以答案不对,我做出假设,那5个数中间就有第一个数。那么剩下的数之和就为-8。果然找到了。dp[4][-8 + 6*20] = 92,分别是第2,4,8,9个数的和。结果已经出来了。这个程序坑爹的bug已经完全的暴露在我们面前了。
就是:它无法使几组加起来大小相同,且总的和相同的数据共存!有人可能说,这有什么关系??关系大了,假设答案是一组数据加另外一组数据中的一个数,而程序的 那个位置上选择的是后者。那就永远不可能得出正确答案,这种数据挺难想出来,所以被绝大多数人忽略。
在这里十分佩服 CZDleaf。在题目的discuss里提出了疑问无疑是正确的,我是一个刚开始做DP的菜鸟,想了好几天才想明白,DP真是个死脑细胞的东西啊。
大牛们肯定一点就通,接下来我就简单说说如何使用2维DP解这道题。灰常简单啊!!(还是问了学长)只要把枚举N组数据放在最外面,里面用逆推,依据背包里的思想。这样就不会有重复,还有关键的一点,就是那个错误程序的bug在这个程序有木有解决,当然解决啦!在这里数没有那个重复选择的影响,所以假设数字编号135和246是加起来一样,和也一样(和跟加起来是2种意思,看warning),而答案是1356,拿只笔模拟一下就会发觉根本一点关系都没有,因为是顺序的,所有组合都会遍及到不会有覆盖情况,还是因为顺序的所以连sort函数都省了。
其他具体的就看我的AC代码吧~
有问题欢迎大家留言讨论~~
#include
#include
#include
#include
#include
#include
using namespace std;
int dp[21][801];
vector path[21][801];
int main()
{
int times=1;
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
int subtraction[201],_plus[201];
int n,m,i,j,k;
while(~scanf("%d%d",&n,&m) && n && m)
{
for(i=0;i>d>>p;
subtraction[i] = d-p;
_plus[i] = d+p;
}
int fix = 20*m;
dp[0][fix] = 0;
for(k = 0; k < n; k++)//选择一个
for(i = m-1; i >= 0; i--)//进行逆推
{
for(j = 0; j < 2*fix; j++)
{
if(dp[i][j] >= 0)
{
if(dp[i+1][j+subtraction[k]] <= dp[i][j] + _plus[k])
{
dp[i+1][j+subtraction[k]] = dp[i][j] + _plus[k];
path[i+1][j+subtraction[k]] = path[i][j];//每次更新都要把path全部复制过来,就是因为这个才用的vector
path[i+1][j+subtraction[k]].push_back(k);
}
}
}
}
for(i = 0; dp[m][fix+i] == -1 && dp[m][fix-i] == -1; i++);
int temp = (dp[m][fix+i] > dp[m][fix-i]) ? i : -i;
int sumD = ( dp[m][fix+temp] + temp )/2;
int sumP = ( dp[m][fix+temp] - temp )/2;
printf( "Jury #%d\n", times++ );
printf( "Best jury has value %d for prosecution and value %d for defence:\n", sumD,sumP);
for( i=0; i < m; i++ )
printf( " %d", path[m][fix+temp][i]+1);
printf( "\n\n" );
}
return 0;
}