刚学了Dinic就开始做题,然后就崩了。
题意:若干个任务,可以放在两个CPU中任意一个上完成,各有一定代价。其中又有若干对任务,如果它们不在同一个CPU上完成,会产生额外代价。最小化并输出代价。
一开始的想法是吧一个任务拆开成两个点(受2-sat的影响),然后就发现自己没法做了。首先,需要保证由同一个任务拆成的两个点只流过一个,这就会使最大流变成费用流。(我不会费用流)其次,难以满足第二个要求。后来一想,网络流与2-sat建图的区别在于网络流的状态表现在边的流量上(是否满流),而2-sat表现于同组点的取舍上。同样地,我也不能在2-sat和单位网络之间划等号。前者的作用是生成一个可行的方案,后者则是最优解。
那么,如上文所述,我们见图时要把状态表现在流量上。先不考虑额外代价,对于一个任务,它应该接着两条边表示两种选择。于是,可以从题解上抄到想到从超级源点向它建一条容量为第一种选择代价的边,从它向超级汇点建一条容量为第二种选择的边。此时这张图的最小割就是最优解。
然后考虑额外代价。存在这个代价当且仅当两个任务处割掉了位置不同的边。
左上图:两个任务处割掉了位置不同的边
然后,我们只要在两个任务之间建一条容量为额外代价的双向边就可以了。这时因为割的性质就能把额外代价考虑其中了。并且,这不会影响其他情况。
故我们完成了建图,然后就只需要贴板子卡常就可以了。
令人震惊的是:时间复杂度O(n^2*m)。代码已自动过滤快读、register、inline
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <queue> 5 using namespace std; 6 const int N = 20010, M = 240005, INF = 1<<30; 7 int n,m,s,t; 8 struct edge { 9 int la,b,cap,flo; 10 edge(int la=0,int b=0,int cap=0,int flo=0): 11 la(la),b(b),cap(cap),flo(flo){}; 12 }con[M<<1]; 13 int tot,fir[N],cur[N],d[N]; 14 void add(int from,int to,int cap,int cap_back=0) { 15 con[++tot] = edge(fir[from],to,cap); 16 fir[from] = tot; 17 con[++tot] = edge(fir[to],from,cap_back); 18 fir[to] = tot; 19 } 20 bool vis[N]; 21 void init() { 22 for (int i=1;i<=n;++i) cur[i] = fir[i]; 23 memset(vis,0,sizeof vis); 24 } 25 bool bfs() { 26 init(); 27 queue<int> q; 28 while (!q.empty()) q.pop(); 29 q.push(s); 30 d[s] = 0; 31 vis[s] = 1; 32 for (int pos = q.front();!q.empty();q.pop(),pos = q.front()) { 33 for (int i=fir[pos];i;i=con[i].la) if (!vis[con[i].b]) { 34 if (con[i].cap>con[i].flo) { 35 d[con[i].b] = d[pos] + 1; 36 vis[con[i].b] = 1; 37 q.push(con[i].b); 38 } 39 } 40 } 41 return vis[t]; 42 } 43 int dfs(int x,int imp) { 44 if (x == t||!imp) return imp; 45 int expo = 0, tmp; 46 for (int &i=cur[x];i;i=con[i].la) if (d[con[i].b] == d[x] + 1) { 47 tmp = dfs(con[i].b,min(imp,con[i].cap-con[i].flo)); 48 if (tmp > 0) { 49 con[i].flo += tmp; 50 con[i^1].flo -= tmp; 51 expo += tmp; 52 imp -= tmp; 53 if (!imp) break; 54 } 55 } 56 return expo; 57 } 58 int solve() { 59 int res = 0; 60 while (bfs()) { 61 res += dfs(s,INF); 62 } 63 return res; 64 } 65 int main() { 66 int a,b,w; 67 while (scanf("%d%d",&n,&m)!=EOF) { 68 memset (fir,0,sizeof fir); 69 tot = 0; 70 for (int i=1;i<=n;++i) { 71 scanf("%d%d",&a,&b); 72 add(n+1,i,a); 73 add(i,n+2,b); 74 } 75 for (int i=1;i<=m;++i) { 76 scanf("%d%d",&a,&b,&w); 77 add(a,b,w,w); 78 } 79 s = n+1, t = n+2; 80 n += 2; 81 printf("%d ",solve()); 82 } 83 return 0; 84 }
小结:这可以说是对网络流模型的理解还不够。