纸牌问题

146 阅读2分钟

本人已参与[新人创作礼] 活动,一起开启掘金创作之路 [问题描述] 有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有 若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌, 然后移动。移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号 为 2 的堆上; 在编号为 N 的堆上取的纸牌, 只能移到编号为 N-1 的 堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。现在要 求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。 例如 N=4,4 堆纸牌数分别为: ① 9 ② 8 ③ 17 ④ 6 移动 3 次可达到目的: 从 ③ 取 4 张牌放到 ④ (9 8 13 10) -> 从 ③ 取 3 张 牌放到 ②(9 11 10 10)-> 从 ② 取 1 张牌放到①(10 10 10 10)。 其实就是贪心:

````js`


`#include<stdio.h>
#include<string.h>
int main()
{
    int ncase,i,n;
    scanf("%d",&ncase);
    while(ncase--)
    {
        int arr[1000],sum=0,v,num=0;
        memset(arr,0,sizeof(arr));
        scanf("%d",&n);
        for(i=0;i<n;i++)
         {
             scanf("%d",&arr[i]);
             sum+=arr[i];
         }
         v=sum/n;
         for(i=0;i<n-1;i++)
         {
             if(arr[i]<v){arr[i+1]=arr[i+1]-v+arr[i];num++;}
             if(arr[i]>v){arr[i+1]=arr[i+1]+v-arr[i];num++;}
         }
         printf("%d\n",num);
 
    }
    return 0;
}
#include<stdio.h>
#include<string.h>
int temp;
void move(int i,int v,int s[100],int d[100])
{
    if(i<0||d[i]==0)    //i<0,不用解释了,d[i]==0代表前i堆都分配好了
        return;
    if(d[i]==1)            //从后一堆拿
    {
        printf("从第%d堆纸牌向%d堆纸牌移动%d张!\n",i+1,i,v-s[i]);
        move(i-1,v,s,d);    //递归
        d[i]=0;                //置零,其实一次递归就可以让前i堆都分配成功!
    }
    else if(d[i]==2)    //给后一堆
    {
        printf("从第%d堆纸牌向%d堆纸牌移动%d张!\n",i,i+1,s[i]-v);
        d[i]=0;
    }
}`
//处理当前i堆函数。。。其实我自己也不知道怎么定义这个函数才对
/*
参数说明:
i:当前堆
v:平均值
s:堆状态序列值
d:记录分配情况:1代表向后一堆取,2代表向后一堆给,0代表本堆已经是平均值了,没有操作
n:堆数
*/
void mypow(int i,int v,int s[100],int d[100],int n)
{
    if(i==n)            //前n-1堆分配成功后,第n堆一定是平均值,所以直接返回
        return;
    if(s[i]<v)            //如果本堆小于平均值,就向前一堆去要
    {
         d[i]=1;
         if(s[i+1]-(v-s[i])>=0)        //如果前一堆可以给,就直接移动,事实上这次给可以让前i堆纸牌值全部为平均值
         {
              temp=1;
              move(i,v,s,d);        //移动函数
         }
        s[i+1]=s[i+1]-(v-s[i]);        //第i+1堆状态变化
        mypow(i+1,v,s,d,n);            //递归求解i+1堆
    }
    else if(s[i]>v)                    //如果本堆大于平均值,就给后一堆
    {
        s[i+1]=s[i+1]+(s[i]-v);
        d[i]=2;
        temp=1;
        move(i,v,s,d);                //这里的move其实可以直接写成printf
        mypow(i+1,v,s,d,n);            //递归求解i+1堆
    }
    else if(s[i]==v)                //如果本堆等于平均值,当然不用移动了
    {
        d[i]=0;
        mypow(i+1,v,s,d,n);            //递归求解i+1堆
    }
    return;
}
int main()
{
    int n,sum,v;
    int s[100],d[100];
    int i;
    int count;
    while(scanf("%d",&n)!=EOF&&n!=0)
    {
        memset(d,0,sizeof(d));
        sum=0;
        v=0;
        count=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&s[i]);
            sum=sum+s[i];
        }
        v=sum/n;
        temp=0;
        mypow(1,v,s,d,n);
        if(!temp)                //这个就是不用分配标记
            printf("不用移动了!\n");
    }
    return 0;
}