【题目链接】
http://codeforces.com/problemset/problem/289/B
【解题报告】
CF round 177 div2.
题目大意,给你一个N*M的矩阵,里面每个元素有一个val,你可以每次对它+d或者-d,问至少需要多少次操作,使得所有元素val相同。如果不可能,输出-1.
这道题做比赛的时候wa了五次。想对了一点,就是给定的n个数一定是模d的剩余相同。但是具体求最少操作的时候,我把元素值相同的点都合并了,然后误以为这其实是一个给定线段上N个点,求所有点到一个点的最小距离的一个题目,然后直接按照中间点是最优解的方法来做了。
但实际上,我没有考虑到,当某个点出现多次是,它的权重就会增加,那么它到别的点的代价就会增大,就不能单纯按线段上的点来考虑了。
所以这道题应当枚举每个点作为最终目标的时候,所需要的总操作数。
维护一个l[i]表示把第i个数左边的数变的都和它相同需要多少操作。
维护一个r[i]同理。
这可以在线性时间内得到。
之后扫一遍l[i]+r[i]即可。
应当说,因为比赛经验实在太少,所以面对构造算法模型的题目会想的很慢,而且极易想错或者忽略一些细节和边界条件。这几次比赛,包括去地大和北航的个人赛都只能过签到题,对稍微需要用点思维的题就会写崩,频频用很复杂的思维或者写很复杂的没必要的算法来想题做题,都体现了这一弱点。之后应当保持做个人赛的频率,提高比赛的感觉和构造能力的提高。
打得差不可怕,可怕的不能遭受挫折不愿正视缺点,妄自菲薄,妄自尊大。这样的道理很简单,却容易当局者迷。
忽然想到《灌篮高手》里,年轻的三井说:“教练,我想打篮球”,十分难过。只有年轻人才会执着于“梦想”,难以放弃的坚持才是对梦想的最重要的诠释。
所以把叹息留在身后,重新上路吧。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
int N,M,d;
int a[10000+10];
int l[10000+10],r[10000+10];
int main()
{
cin>>N>>M>>d;
for( int i=1; i<=N; i++ )
for( int j=1; j<=M; j++ )
{
int t=(i-1)*M+j;
cin>>a[t];
}
sort( a+1,a+1+N*M );
int temp=a[1];
for( int i=1; i<=N*M; i++ ) //检查是否合法
{
a[i]-=temp;
if( a[i]%d ){ cout<<-1<<endl; return 0; }
}
for( int i=1; i<=N*M; i++ )
{
l[i]=l[i-1]+ (i-1)*( a[i]-a[i-1] )/d;
}
for( int i=N*M; i>=1; i-- )
{
r[i]=r[i+1]+ ( N*M-i )*( a[i+1]-a[i] )/d;
}
int ans=1e9;
for( int i=1; i<=N*M; i++ )
{
ans=min( ans, l[i]+r[i] );
}
cout<<ans<<endl;
return 0;
}