CDN流量調度問題
題看了沒多久就看出來是\(DP\)的題,然后就設了狀態\(f[i][j]\)表示到前\(i\)個點時已經用了\(j\)個節點的最小總代價,結果發現轉移時\(O(nm^2)\),但這樣只會T掉的,於是就順利應當的進入了DP優化的思維,奈何無論用上什么伎倆都好像有點不太奏效,以下給出暴力的代碼:
rep(i,1,n) rep(j,0,m)
rep(k,1,min(j+1,t[i]))//枚舉i的節點數量。
f[i][j]=min(f[i][j],f[i-1][j+1-k]+a[i]/k+(a[i]%k?1:0));
發現難點就在於轉移時,\(a[i]/k\)(向上取整)這個沒法很好的處理....之后看來看題解,發現果然是在這里做文章的,這里考慮我們暴力的做法k的范圍從1到min(j+1,t[i]),相當於將所有可能的都枚舉了。但再仔細的瞅一眼上面這個式子,\(a[i]/k=t\)(這里向上取整)。也就是說\(a[i]=k*t\)那\(k\)和\(t\)不就是\(a[i]\)的因子嗎?不不不...這里由於是向上取整的緣故,所以不是嚴格意義下的因子,因為我們可以發現對於任意一個數,它都能除以其他數在向下取整的情況下。那我們怎么辦呢?考慮到是向上取整的緣故,所以一定有\(k*t>=a[i]\),並且要求\(k\)和\(a[i]\)一定時,\(t\)最小,這樣的話,我們也可以通過枚舉所謂“因子”的方法枚舉\(k\),若因為k和t是成對存在的我們枚舉小的那個,這樣的話,我們枚舉的范圍就是\(\sqrt{a[i]}\)。
怎么說呢,這個題反正到最后還是有點不很理解的...
為什么這個k得枚舉范圍就降了一個\(\sqrt{a[i]}\),大概可以這么說吧,就是你通過分析這個\(a[i]/k\)向上取整這個得值最多是2*\(\sqrt{a[i]}\),所以有很多的k,\(a[i]/k\)向上取整對應的值都一樣的,這樣的情況下,你就根本沒必要去枚舉那些多余的k值,你只需要知道\(a[i]/k\)有多少個值,並且他們所對應的最小的k就行了,大的k但和他們貢獻相同的就不用枚舉了....通過預處理,可以提前縮短我們遍歷的狀態空間,可以說這個題給我帶來的啟示很大!
//不等,不問,不猶豫,不回頭.
#include<bits/stdc++.h>
#define _ 0
#define ls p<<1
#define db double
#define rs p<<1|1
#define P 1000000007
#define RE register
#define ll long long
#define INF 1000000000
#define get(x) x=read()
#define PLI pair<ll,int>
#define PII pair<int,int>
#define ull unsigned long long
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
#define go(x) for(RE int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=105,M=10050;
int T,n,m,a[N],t[N],f[N][M],size[N];//f[i][j]表示前i個線路用了j的節點的最小代價。
PII v[N][M];//預處理出每個線路的所有不同的向上取整的結果已及所需的節點數。
inline int read()
{
int x=0,ff=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*ff;
}
int main()
{
//freopen("1.in","r",stdin);
get(T);
while(T--)
{
get(n);get(m);
rep(i,1,n) get(a[i]);
rep(i,1,n) get(t[i]);
memset(f,0x3f,sizeof(f));
memset(size,0,sizeof(size));
f[0][0]=0;
rep(i,1,n)
{
int temp=0;
rep(j,1,t[i])
{
if(a[i]/j+(a[i]%j?1:0)!=temp)
{
temp=a[i]/j+(a[i]%j?1:0);
v[i][++size[i]]={temp,j};
}
}
}
rep(i,1,n) rep(j,0,m)
{
if(j) f[i][j]=min(f[i][j],f[i][j-1]);
rep(l,1,size[i])
{
if(v[i][l].second-1>j) break;
f[i][j]=min(f[i][j],f[i-1][j+1-v[i][l].second]+v[i][l].first);
}
}
put(f[n][m]);
}
return (0^_^0);
}
//以吾之血,鑄吾最后的亡魂.