目录
1.描述
两人给每块大理石分配一个价值:从数字1至6。 现在他们尝试分配石块,使得每人得到的大理石块总价值相同。请你写一个程序,判断是否能公平分配大理石块。
2.数据预处理
将各种价值大理石的块数,存放到数组int a[7]中,价值为i(1≤i≤6)的大理石块个数为a[i](0≤a[i]≤20000)
对任意价值的大理石块,无论增加多少个2,仍然是可以平分的。
不需要对每种价值的大理石,按多达20000块进行平分,只需要对一个最低限度的大理石块数n(0≤n≤a[i])进行平分,其余的部分,他们直接平分即可。 经过在线测试,n=6。
核心算法如下:
a[i]=
- 6 a[i]>0且是6的倍数
- a[i]%6 else
3.思路
两个人能否平分大理石块就是一个人能否从总价值中取出一半价值的大理石块。
设大理石块总价值为sum,根据
- 若sum为奇数,不可平分
- 否则令mid为大理石块总价值的一半
我们只关心从0~mid的价值,存储到数组bool visit[200]
a[i]≤6(1≤i≤6),sum最大是6×(1+2+3+4+5+6)=128,则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])块大理石,该平分方案仍然是成立的。
- visit[0]:a什么都不分,b拿走全部大理石
- visit[0+i*k]:b给a分了价值为i的k块大理石,a拥有i*k价值的大理石(1<=k<=a[i]),b拿剩下的大理石
- visit[sum]:a拿走全部大理石,b什么都不分
4.代码
动态规划:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int a[7];//各种价值大理石的块数
void dp(int sum){
int i,j,k;
int mid=sum/2;
char visit[200];//大理石的状态数组
memset(visit,0,sizeof(visit));
//将visit数组用0初始化
int t;
visit[0]=1;
//i=1,只考虑价值为1的大理石
//i=2,考虑价值1,2的大理石
for(i=1;i<=6;i++)
//倒推
for(j=mid;j>=0;j--)
if(visit[j])
//价值为i的大理石,从1块到a[i]块分
for(k=1;k<=a[i];k++){
t=j+i*k;
//判断这种分法是否成立
if(t>mid) break;
else visit[t]=1;
//分法成立的前提下,判断分的两份是否相等
if(t==mid){
printf("Can\n\n");
return;
}
}
printf("Can't\n\n");
}
int main()
{
int icase=1;//测试数据组的编号
//数据预处理
while(1){
int sum=0;//预处理之后大理石的总价值
for(int 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;
}
if(sum==0)break;
printf("Collection #%d:\n",icase++);
//总价值为奇数,无法平分
if(sum%2){
printf("Can't\n\n");
continue;
}
dp(sum);
}
return 0;
}
枚举法:
void mj(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[1];i4++)
for(i5=0;i5<=a[2];i5++)
for(i6=0;i6<=a[3];i6++){
mid=1*i1+2*i2+3*i3+4*i4+5*i5+6*i6;
if(mid==sum/2){
printf("Can");
return;
}
}
printf("Can't");
}