5 4 12 10 4 3 5
给5个数,每次取两个数减
con([12,10,4,3,5],2) = [12,6,3,5] con([12,6,3,5] ,3) = [12,6,-2] con([12,6,-2] ,2) = [12,8] con([12,8] ,1) = [4]
最终获得4
解法:
原问题可视为在数列中添加减号和括号
则可转化为添加加号和减号
动态规划
d[i][j]表示前i个数获得j结果是否可能
状态转移为
d[i][j]=d[i-1][j-a[i-1]||d[i-1][j+a[i-1]
最后把加减序列提取出来转化成最后结果
转化方式摘自DISCUSS:
while 序列长度大于二 do if 序列第三个是+ then 输出2 删除序列第三个 else 输出1 删除序列第二个 end if end while 输出1以下是代码:
#include<cstdio>
#include<algorithm>
#include<list>
#include<cstring>
using namespace std;
bool d[110][20005];//d[i][j]表示前i个数收缩的结果为j
int a[110];
bool path[110];//+代表true
#define M 10000
int main()
{
int n,T;
scanf("%d%d",&n,&T);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
memset(d,false,sizeof(d));
d[1][a[0]+M]=true;
d[2][a[0]-a[1]+M]=true;
path[1]=false;
int i,j;
for(i=3;i<=n;i++)
{
for(j=0;j<=20000;j++)
{
//d[i][j]=d[i-1][j-a[i-1]]
//d[i][j]=d[i-1][j+a[i-1]]
if(d[i-1][j-a[i-1]])
{
d[i][j]=true;
//printf("%d %d\n",i,j-10000);
continue;
}
if(d[i-1][j+a[i-1]])
{
d[i][j]=true;
//printf("%d %d\n",i,j-10000);
continue;
}
}
}
//开始提取+-序列
i=n;
j=T+M;
for(;i>=1;i--)
{
//d[i][j]=d[i-1][j-a[i-1]]
//d[i][j]=d[i-1][j+a[i-1]]
if(d[i-1][j-a[i-1]])
{
path[i-1]=true;
j-=a[i-1];
}
else
{
path[i-1]=false;
j+=a[i-1];
}
}
path[0]=true;
list<bool>L;
for(i=0;i<=n-1;i++)
{
L.push_back(path[i]);
}
while(L.size()>2)
{
list<bool>::iterator p=L.begin();
p++;
p++;
if(*p)
{
printf("2\n");
L.erase(p);
}
else
{
printf("1\n");
p--;
L.erase(p);
}
}
printf("1\n");
return 0;
}