4609: [Wf2016]Branch Assignment 最短路 DP (阅读理解题)
Bzoj的翻译出锅了所以来官方题面:
这个题应该是单向边而BZOJ说的是双向边,什么你WA了?谁叫你懒得看英文......
显然我们能正向反向两遍SPFA处理出每个点到总部的距离和总部到每个点的距离。
如果某个点所在的部门的大小为S,那么这个点需要送出S-1次消息并接收S-1次消息。
我们把每个点的两个距离求和并排序,显然在一个块中的是这个序列上的一个区间(脑补一下为什么不这样不优),我们做一下前缀和。
然后就开始DP了,f[i][j]表示前i个点分j个块,最小代价。f[i][j] = min( f[k][j-1] + ( i - k - 1 ) * ( sum[i] - sum[k] ) )。
这个DP是O(n^3)的,考虑优化。
显然更小的边权所在的块应该更大,所以我们能从区间[i-(i/j),i-1]枚举k,这样能优化到n^2logn。
然而有更优美的做法:显然随着j增大,对于每个i最优的k也是递增的,直接指针扫过去,复杂度O(n^2)。
两种做法都可以AC,反正我6代i5的机器上时间差异不大,不知道BZOJ能不能卡出来(我只交了第一种)。
(为什么我现在在刷这种水题?我也不知道啊!!!)
第一种代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 typedef long long int lli; 7 using namespace std; 8 const int maxn=5e3+1e2,maxe=5e4+1e2; 9 10 lli su[maxn],f[2][maxn]; 11 int b,s,cur; 12 13 struct Graph { 14 int s[maxn],t[maxe],nxt[maxe],l[maxe],dis[maxn],inq[maxn],cnt; 15 inline void addedge(int from,int to,int len) { 16 t[++cnt] = to , l[cnt] = len , nxt[cnt] = s[from] , s[from] = cnt; 17 } 18 inline void spfa(int st) { 19 memset(dis,0x3f,sizeof(dis)) , dis[st] = 0; 20 queue<int> q; q.push(st) , inq[st] = 1; 21 while( q.size() ) { 22 const int pos = q.front(); q.pop() , inq[pos] = 0; 23 for(int at=s[pos];at;at=nxt[at]) 24 if( dis[t[at]] > dis[pos] + l[at] ) { 25 dis[t[at]] = dis[pos] + l[at]; 26 if( !inq[t[at]] ) q.push(t[at]); 27 } 28 } 29 } 30 }gra,rev; 31 32 inline void dp() { 33 for(int i=1;i<=b;i++) su[i] = (lli) gra.dis[i] + rev.dis[i]; 34 sort(su+1,su+1+b) , memset(f,0x3f,sizeof(f)) , **f = 0; 35 for(int i=1;i<=b;i++) su[i] += su[i-1]; 36 for(int j=1;j<=s;j++) { // j is number of groups . 37 cur ^= 1 , memset(f[cur],0x3f,sizeof(f[1])); 38 for(int i=1;i<=b;i++) // i is last node . 39 for(int lst=i/j;lst;lst--) 40 f[cur][i] = min( f[cur][i] , f[cur^1][i-lst] + ( lst - 1 ) * ( su[i] - su[i-lst] ) ); 41 } 42 } 43 44 int main() { 45 static int n,r; 46 scanf("%d%d%d%d",&n,&b,&s,&r); 47 for(int i=1,a,b,l;i<=r;i++) scanf("%d%d%d",&a,&b,&l) , gra.addedge(a,b,l) , rev.addedge(b,a,l); 48 gra.spfa(b+1) , rev.spfa(b+1) , dp(); 49 printf("%lld\n",f[cur][b]); 50 return 0; 51 }
第二种代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 typedef long long int lli; 7 using namespace std; 8 const int maxn=5e3+1e2,maxe=5e4+1e2; 9 10 int tp[maxn]; 11 lli su[maxn],f[2][maxn]; 12 int b,s,cur; 13 14 struct Graph { 15 int s[maxn],t[maxe],nxt[maxe],l[maxe],dis[maxn],inq[maxn],cnt; 16 inline void addedge(int from,int to,int len) { 17 t[++cnt] = to , l[cnt] = len , nxt[cnt] = s[from] , s[from] = cnt; 18 } 19 inline void spfa(int st) { 20 memset(dis,0x3f,sizeof(dis)) , dis[st] = 0; 21 queue<int> q; q.push(st) , inq[st] = 1; 22 while( q.size() ) { 23 const int pos = q.front(); q.pop() , inq[pos] = 0; 24 for(int at=s[pos];at;at=nxt[at]) 25 if( dis[t[at]] > dis[pos] + l[at] ) { 26 dis[t[at]] = dis[pos] + l[at]; 27 if( !inq[t[at]] ) q.push(t[at]); 28 } 29 } 30 } 31 }gra,rev; 32 33 inline void dp() { 34 for(int i=1;i<=b;i++) su[i] = (lli) gra.dis[i] + rev.dis[i]; 35 sort(su+1,su+1+b) , memset(f,0x3f,sizeof(f)) , **f = 0; 36 for(int i=1;i<=b;i++) su[i] += su[i-1]; 37 for(int j=1;j<=s;j++) { // j is number of groups . 38 cur ^= 1 , memset(f[cur],0x3f,sizeof(f[1])); 39 for(int i=1;i<=b;i++) // i is last node . 40 for(int lst=tp[i];lst<i;lst++) 41 if( f[cur][i] >= f[cur^1][lst] + ( i - lst - 1 ) * ( su[i] - su[lst] ) ) f[cur][i] = f[cur^1][lst] + ( i - lst - 1 ) * ( su[i] - su[lst] ) , tp[i] = lst; 42 } 43 } 44 45 int main() { 46 static int n,r; 47 scanf("%d%d%d%d",&n,&b,&s,&r); 48 for(int i=1,a,b,l;i<=r;i++) scanf("%d%d%d",&a,&b,&l) , gra.addedge(a,b,l) , rev.addedge(b,a,l); 49 gra.spfa(b+1) , rev.spfa(b+1) , dp(); 50 printf("%lld\n",f[cur][b]); 51 return 0; 52 }
良心的我给的数据下载:
链接:https://pan.baidu.com/s/19EmgxmCYDASzpTaqr0alkA 密码:00qr
果てないこの闇の向こうにも
在无尽的这黑暗的对面
光があると信じてる
我相信也存在光明
ここから生まれ変わる世界だけ
从此只盯住这脱胎换骨的世界
见つめて离さないよ
目不转睛
広がるこの空を见上げると
仰望这广袤的天空
あの日の戦いが映る
那一日的决战映在脑海
いつかは全て消えてしまうのか
终有一天一切将会消失
栄光を取り戻せ
重新夺回这荣光