1. 程式人生 > 實用技巧 >[loj3342]製作菜品

[loj3342]製作菜品

當$n-1\le m$,不妨令$d_{1}\le d_{2}\le...\le d_{n}$,則$(n-1)k\le mk=\sum_{i=1}^{n}d_{i}\le d_{1}+(n-1)d_{n}$ 將這個拆成兩部分,即$(n-2)k+k$和$(n-2)d_{n}+(d_{1}+d_{n})$,由於後者大於等於前者,所以兩項中必然有一項大於等於前者,容易發現一定存在$k\le d_{1}+d_{n}$ 那麼就可以不斷貪心將$d_{1}$和$d_{n}$匹配,每一次必然會至少消除一個數,而最後一次因為總和恰好為$mk$所以必然全部消除 當$m=n-2$,此時合法當且僅當存在一個集合$S\subset \{1,2,...,n\}$使得$\sum_{i\in S}d_{i}=(|S|-1)k$ 證明:充分性,如果存在,將兩部分分開處理,即有解且已構造得出 必要性,可以證明若存在合法解,必然存在一組使得每次操作使至少一個點變為$0$(顯然消除掉一定更優) 構造:將每一個點向消除掉自己的點連無向邊,顯然這張圖不存在大於2的環(考慮環上操作的先後順序即可) 因此即去除掉重邊後,這張圖是一棵森林且有至少2棵樹(否則有$n-1$次操作),其中任意一棵子樹即可作為集合$S$ 考慮怎麼求出這個集合$S$,暴力$dp$令$f[i][j][k]$表示前$i$個點中選$j$個點和能否為$k$,複雜度為$o(n^{3}k)$無法通過 如何使得其與$|S|$無關,即$S$需滿足$\sum_{i\in S}d_{i}-k=-k$,同時權值範圍僅變為$[-nk,nk]$,因此複雜度降為$o(n^{2}k)$ 問題即一個01揹包的存在性判斷,可以用$bitset$來優化,複雜度降為$o(\frac{n^{2}k}{32})$,可以通過
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 505
 4 set<pair<int,int> >s;
 5 int t,n,m,k,d[N],vis[N];
 6 bitset<N*N*20>f[N];
 7 void work(int m){
 8     for(int i=1;i<=m;i++){
 9         int x=(*s.begin()).second;
10         s.erase(s.begin());
11         if
(d[x]>=k){ 12 printf("%d %d",x,k); 13 d[x]-=k; 14 if (d[x])s.insert(make_pair(d[x],x)); 15 } 16 else{ 17 int y=(*(--s.end())).second; 18 s.erase(--s.end()); 19 printf("%d %d %d %d",x,d[x],y,k-d[x]); 20 d[y]-=k-d[x];
21 if (d[y])s.insert(make_pair(d[y],y)); 22 d[x]=0; 23 } 24 if (i!=m)printf("\n"); 25 } 26 } 27 int main(){ 28 freopen("dish.in","r",stdin); 29 freopen("dish.out","w",stdout); 30 scanf("%d",&t); 31 bool flag=0; 32 while (t--){ 33 if
(flag)printf("\n"); 34 flag=1; 35 scanf("%d%d%d",&n,&m,&k); 36 for(int i=1;i<=n;i++)scanf("%d",&d[i]); 37 if (n-1<=m){ 38 for(int i=1;i<=n;i++)s.insert(make_pair(d[i],i)); 39 work(m); 40 continue; 41 } 42 memset(vis,0,sizeof(vis)); 43 for(int i=0;i<=n;i++)f[i].reset(); 44 f[0][n*k]=1; 45 bool flagg=0; 46 for(int i=1;i<=n;i++){ 47 f[i]=f[i-1]; 48 if (d[i]>=k)f[i]|=(f[i-1]<<d[i]-k); 49 else f[i]|=(f[i-1]>>k-d[i]); 50 if (f[i][(n-1)*k]){ 51 flagg=1; 52 for(int j=i,t=(n-1)*k;j;j--) 53 if (f[j-1][t-(d[j]-k)]){ 54 vis[j]=1; 55 t-=d[j]-k; 56 } 57 for(int j=1;j<=n;j++) 58 if (vis[j])s.insert(make_pair(d[j],j)); 59 work(s.size()-1); 60 printf("\n"); 61 for(int j=1;j<=n;j++) 62 if (!vis[j])s.insert(make_pair(d[j],j)); 63 work(s.size()-1); 64 break; 65 } 66 } 67 if (!flagg)printf("-1"); 68 } 69 return 0; 70 }
View Code