AcWing 105. 七夕祭

原题链接

我愿称lyd为神

考察: 排序+贪心+前缀和

我们将行列分开看,这道题就可以看成均分纸牌的问题,如果纸牌总数不能除尽人数,那么一定无解.

如果可以除尽,本题就是环形分配纸牌问题.

我们利用贪心解题,先想方法让第一个人达到平均数,再想方法让第二个人达到平均数....当计算第i个人,需要的纸牌数是abs(sum[i]-i*mid),因为前i-1个人不管怎么换,都不会影响前i个和,而前i个人需要的纸牌数就是i*mid

但是本题是可以环形分配纸牌的,也就是参考断环为链的思想.改变将每个人的位置依次往左循环挪动,就可以发现前缀和改变可以得到更优的交换次数

根据lyd大佬的方法,假设第k个人后断环,可以列出前缀和改变后每个元素的前缀和公式,发现每个人的前缀和都变成了sum[i]-sum[k].(i+k>m的通过使sum[m]==0使得公式一致)

这就与上题的货仓选址是一样的了

 1 #include <iostream>//lyd大佬真的太神了
 2 #include <algorithm>
 3 #define ll long long
 4 using namespace std;
 5 const ll N = 100010;//均分纸牌问题之二维版
 6 int n,m,t;
 7 ll row[N],col[N],f[N];
 8 ll calc(ll a[],int cnt)
 9 {
10     ll ans = 0;
11     for(int i=1;i<=cnt;i++){
12         a[i] -= t/cnt;
13         f[i]=f[i-1]+ a[i];
14     }
15     sort(f+1,f+cnt+1);
16     for(int i=1;i<=cnt;i++) ans+=abs(f[i]-f[cnt/2+1]);
17     return ans;
18 }
19 int main()//错误1:此均分纸牌是环形的,i可以和i-1和i+1交换,可以调整挪动每个人的位置以获取最优解
20 {
21     scanf("%d%d%d",&n,&m,&t);
22     ll ans = 0;
23     int tmp = t; bool rflag = true,cflag = true;
24     while(tmp--){
25         int x,y; 
26         scanf("%d%d",&x,&y);
27         row[x]++; col[y]++;
28     }
29     if(t%n==0) { ans+=calc(row,n); rflag = false;}
30     if(t%m==0) { ans+=calc(col,m); cflag = false;}
31     if(rflag&&cflag) printf("impossible\n");
32     else if(!rflag&&cflag) printf("row %lld\n",ans);
33     else if(!cflag&&rflag) printf("column %lld\n",ans);
34     else printf("both %lld\n",ans);
35     return 0;
36 }

 

posted @ 2021-01-05 16:37  acmloser  阅读(80)  评论(0编辑  收藏  举报