Dividing(dp)

题目描述

Marsha和Bill搜集到一些大理石块,他们想要平均分配这些石块。如果每块石头的价值都一样,那么事情显得异常简单。但是一些大理石块比较大,或更漂亮一些。
Marsha和Bill给每块大理石分配一个价值:从数字1至6。 现在他们尝试分配石块,使得每人得到的大理石块总价值相同。
他们明白就这样分配大理石块仍然是很困难的(即使所有大理石块的总价值是偶数)。
例如,价值为1的大理石块一个、价值为3的大理石块一个,和价值为4的大理石块两个,他们就无法把大理石块按总价值平分。因此他们请你写一个程序,判断是否能公平分配大理石块。
在这里插入图片描述

数据预处理

将各种价值大理石的块数,存放到数组中: int a[7];
价值为i(1≤i≤6)的大理石块个数为a[i](0≤a[i]≤20000)。
对任意价值的大理石块,无论增加多少个2,仍然是可以平分的,因为只要平分这个2就可以了。
不需要对每种价值的大理石,按多达20000块进行平分,只需要对一个最低限度的大理石块数n(0≤n≤a[i])进行平分,其余的部分,他们直接平分即可。
显然n是偶数,经过在线测试,n的值很小,n=6。
核心算法如下:
在这里插入图片描述
其中:1≤i≤6。

int a[7];		//各种价值大理石的块数
int iCase = 1;		//测试数据组的编号
int sum = 0;		//预处理之后大理石的总价值
int i;
for (i=1; i<=6; i++)
{
 scanf("%d",&a[i]);
 //数据预处理
 if(a[i]!=0 && a[i]%6==0) a[i] = 6;
 else a[i] = a[i]%6;
 sum += a[i]*i;
}
//当和为0时,说明是输入结束标志
if (sum==0) break;
printf("Collection #%d:\n", iCase++);
//总价值为奇数,无法平分
if(sum%2) 
{
 printf("Can't be divided.\n\n");
 continue;
}

在这里插入图片描述

使用动态规划算法平分

两个人能否平分大理石块,可以简化为一个人能否从大理石块的总价值中取出一半价值的大理石块。
设大理石块总价值为:
在这里插入图片描述
若sum为奇数,是不可能实现平分的;
否则令mid为大理石块总价值的一半:int mid = sum/2;

将所有大理石的价值进行组合,可以构造出各种价值,我们只需要关心从
0~mid的价值,即根据大理石的价值划分动态规划的阶段,
存储到数组中:bool visit[200];
因为a[i]≤6(1≤i≤6),则mid≤63。
当visit[j]=true(0≤j≤mid)时,表示有一个人获得价值j,另一个人获得价值sum-j的大理石块分配方案。
若visit[j]=false,说明这种分配方案不存在。
我们的任务就是计算出visit[mid]是true还是false。
显然有visit[0]=true的方案存在,即一个人什么都不分,另外一个人拿走全部的大理石。
设i(1≤i≤6)为大理石块的价值,若visit[j]=true(0≤j≤mid),则增加k(1≤k≤a[i])块大理石,该平分方案仍然是成立的。

void DP(int sum)
{
 int i,j,k;
 int mid = sum/2;
 //大理石的状态数组
 char visit[200];
 memset(visit, 0, sizeof(visit));
 int t;
 visit[0] = 1;
  //对每种价值的大理石块
 for(i=1; i<=6; i++)
  for(j=mid; j>=0; j- -)
   //价值为j的分配方案存在
   if (visit[j])
    for(k=1; k<=a[i]; k++)
    {
     t = j+i*k;
     if (t>mid) break;
      //增加k块价值为i大理石,该平分方案仍然是成立的
     else visit[t] = 1;
     if (t==mid) {	//找到了分配方案
      printf("Can be divided.\n\n");
      return;
     }
    }
 printf("Can't be divided.\n\n");
}

在这里插入图片描述

枚举算法

//形参sum是大理石块总价值
void enumerate(int sum)
{
 int mid;
 int i1,i2,i3,i4,i5,i6;
 for(i1=0; i1<=a[1]; i1++)
 for(i2=0; i2<=a[2]; i2++)
 for(i3=0; i3<=a[3]; i3++)
 for(i4=0; i4<=a[4]; i4++)
 for(i5=0; i5<=a[5]; i5++)
 for(i6=0; i6<=a[6]; i6++)
 {
  mid = 1*i1+2*i2+3*i3+4*i4+5*i5+6*i6;
  if(mid==sum/2) {
   printf("Can be divided.\n\n");
   return;
  }
 }
 printf("Can't be divided.\n\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值