[noip2002]: 均分纸牌

11 篇文章 0 订阅
6 篇文章 0 订阅

描述

有 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)。

格式

输入格式

N(N 堆纸牌,1 <= N <= 100)
A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000)

输出格式

所有堆均达到相等时的最少移动次数。

样例1

样例输入1

4
9 8 17 6

 
 

样例输出1

3

 
 

限制

每个测试点1s

来源

NOIP2002提高组第一题


题解

简单题 算出平均数以后直接加加减减 写这个题的目的不是这个

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int a[150];
int main(){
	int n,sum=0,tot;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum+=a[i];	
	} 
	tot=sum/n;int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]==tot) continue;
		ans++;
		a[i+1]+=a[i]-tot;
	}
	printf("%d",ans);
	return 0; 
} 
目的是另一种做法:

费用流。。

(noip本来就不考网络流 而且这么简单的noip提高组第一题用什么费用流,还几乎搞了一下午真是浪费时间

感觉可以写就写了试试  此题和网络流24题里面的负载平衡问题差不多

结果发现因为 这里的费用是每一次才加1 而且如果有重复的情况不好处理(看样例就行 此代码过不了样例。。。)

在这里提供一种思路 如果哪位大神看到这个博客 可以改进我的代码请留言或QQ我

虽然这个做法不行,但是因为数据水的问题还是 得()了40分

//因为费用流过程中 每次找到一个最短路 但是有些操作时重复 所以无法通过 
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define inf 0x7ffffff
using namespace std;
struct node{int to,next,flow,cost;}e[1000005];
int start,end,flag[1005],d[1005],from[1005],head[1005],a[150],mp[300][300];
queue<int>q;int cnt=1;int min_cost;
void ini(int x,int y,int flow,int cost){e[++cnt].to=y;e[cnt].flow=flow;e[cnt].cost=cost;e[cnt].next=head[x];head[x]=cnt;}
void insert(int x,int y,int flow,int cost){ini(x,y,flow,cost);ini(y,x,0,-cost);}
bool spfa(){
	memset(d,127,sizeof(d));
	d[start]=0;
	q.push(start);flag[start]=1;
	while(!q.empty()){
		int k=q.front();q.pop();flag[k]=0;
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(e[i].flow&&d[kk]>d[k]+e[i].cost)
			{
				d[kk]=d[k]+e[i].cost;from[kk]=i;
				if(!flag[kk]){
					flag[kk]=1;
					q.push(kk);
				}
			}
		}
	}
	return d[end]<inf;
}
void mcf(){
    while(spfa()){
        int mn=inf;
        for(int i=from[end];i;i=from[e[i^1].to]){
            mn=min(mn,e[i].flow);//printf("%d ",e[i].to);
        }
        min_cost+=d[end];
        for(int i=from[end];i;i=from[e[i^1].to]){
            e[i].flow-=mn;e[i^1].flow+=mn;
        }//printf("\n");
    }
    
}
int main(){
	int n,sum=0,tot;
	start=0,end=1001;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		sum+=a[i];
	}
	tot=sum/n;
	for(int i=1;i<=n;i++)
		a[i]-=tot;
	for(int i=1;i<=n;i++){
		if(a[i]>0) insert(start,i,a[i],0);
		else if(a[i]<0) insert(i+n,end,-a[i],0);
		if(i!=n){
			insert(i,i+1,inf,1);
			insert(i,i+1+n,inf,1);
		}
		if(i!=1)
		{
			insert(i,i-1,inf,1);
			insert(i,i-1+n,inf,1);
		}
		//insert(i+n,i,inf,0);
	}
	mcf();
	printf("%d",min_cost);
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值