1. 程式人生 > >【模擬賽-普及day1t1】打飯(dp)

【模擬賽-普及day1t1】打飯(dp)

【題目描述】 有 n 個小朋友來食堂吃飯,他們排成長度為 n 的一個隊伍。小朋友看到別的小朋友比自己菜好會不高興,兩個小朋友i和j能互相看到對方的菜當且僅當abs(i-j)=k,產生的不高興值是abs(a[i]-a[j]), abs是取絕對值符號,ai表示第i位小朋友菜豐盛的程度如果小朋友產生的不高興值和太大他們會哭,你現在有n盤菜,請你合理安排上菜順序,使得產生不高興值的和最小,輸出這個值 【輸入檔案】 第一行輸入兩個正整數n,k 第二行n個整數,表示第i盤菜的豐盛值ai 【輸出檔案】 一行一個數表示最小的不高興值和 【樣例輸入輸出】 樣例1 輸入 3 2 1 2 4 輸出 1

樣例2 輸入 5 2 1 3 1 3 1 輸出 0

樣例3 輸入 6 3 4 3 4 3 2 5 輸出 3 【樣例解釋】 對於第一個樣例,最優解可以為1,4,2。答案為1 對於第二個樣例,不要改變順序,答案為0 對於第三個樣例,最優解可以為4,3,2,4,3,5,答案為3 【資料規模】 對於30%的資料,滿足n<=10 對於50%的資料,滿足n,k<=5000 對於另外30%的資料,滿足n是k的倍數 對於100%的資料,滿足n<=300000,k<=min(n-1,5000),-10^9<=ai<=10^9  

這題拿到一看N=3e5,直覺是二分+貪心。開始的寫法是,將陣列a排序,然後二分這個最小的和,check時就貪心地將相鄰的a[i]放在距離k的位置上。考完同學說二分沒用,仔細一想確實是這樣,貪心地放,方案是不變的,而且有一個while寫錯全TLE就很尷尬了。反正就是傻了,一套模擬題怎麼會沒有dp題呢。

正解:

dp的話,dpN是不可能的,但是看到K的數字很小(考試沒注意看K,以為最大和N一樣),就有點分組然後dpK的意思了。

有這幾件事是顯然的:最後N道菜被分成了K組,其中有N%K組的菜數量為N/K+1,其餘的數量為N/K。

其次,分在一組的菜一定在排序後的a數組裡是連續的,可以使得差值和最小。

最後,把這一問題抽象為:一個排好序的陣列,有N個數字,把陣列分為K段,每段長為N/K或者N/K+1,且長度為N/K+1的段數不超過N%K,求所有段中前後差值和,的最小值。

這樣一來就容易想到設f[i][j]表示前i段有j段的長度為N/K+1時,差值和的最小值。

f[i][j]可以由f[i-1][j]和f[i-1][j]轉移來,前者表示第i段的長度為N/K,後者表示長度為N/K+1。可以做一個字首和,轉移時直接加一段區間就可以了。

細節需稍加思考。 

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=300005;
const int MAXK=5005;
const LL INF=1e9*3e5;

int N,K,a[MAXN*2];
LL sum[MAXN*2],f[MAXK][MAXK];

char c;int flag;
void scan(int &x){
	flag=0;
	for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
	if(c=='-')c=getchar(),flag=1;
	for(x=0;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
	if(flag)x=-x;
}

int main(){
	scan(N);scan(K);
	for(int i=1;i<=N;i++)scan(a[i]);
	sort(a+1,a+1+N);//a[0]=a[1];
	for(int i=1;i<=N;i++)sum[i]=sum[i-1]+(LL)a[i]-a[i-1];
	for(int i=N+1;i<=N*2;i++)sum[i]=sum[N];
	int A=K,B=N%K,len=N/K,pos;
	for(int i=1;i<=A;i++){
		pos=len*(i-1);
		f[i][0]=f[i-1][0]+sum[pos+len]-sum[pos+1];
		for(int j=1;j<=min(B,i);j++){
			f[i][j]=INF;
			if(i-1>=j)f[i][j]=min(f[i][j],f[i-1][j]+sum[pos+len+j]-sum[pos+1+j]);
			f[i][j]=min(f[i][j],f[i-1][j-1]+sum[pos+len+j-1+1]-sum[pos+1+j-1]);
		}
	}
	printf("%lld",f[A][B]);
	return 0;
}