bz3112 [Zjoi2013]防守战线
这个题挺厉害的,一道线性规划和网络流结合的经典模型。
那样例来解释吧:
Min->X1+5*X2+6*X3+3*X4+4*X5
X2+X3≥1
X1+X2+X3+X4+X5≥4
X3+X4+X5≥2
X1,X2,X3,X4,X5≥0
我们可以利用对偶原理把它转化成标准形式:
Max->Y1+4*Y2+2*Y3
Y2≤1
Y1+Y2≤5
Y1+Y2+Y3≤6
Y2+Y3≤3
Y2+Y3≤4
增加松弛变量,把标准形式变成松弛形式
Max->Z=Y1+4*Y2+2*Y3
Y2+Y4=1
Y1+Y2+Y5=5
Y1+Y2+Y3+Y6=6
Y2+Y3+Y7=3
Y2+Y3+Y8=4
到这里,其实我们已经可以利用单纯形来解决了。这样做速度又快,代码量也很优秀。
接下来我们主要来研究一下网络流
分别用下面一个式子减去上面一个式子,得到:
Max->Z=Y1+4*Y2+2*Y3
Y2+Y4=1
Y1+Y5-X4=4
Y3+Y6-Y5=1
Y7-Y1-Y6=-3
Y8-Y7=1
-Y2-Y3-Y8=-4
这样我们发现,每个变量在所有式子中正负只出现了一次,正好对应了网络流流量守恒的约束。于是对于每个式子建立一个点,那么每个变量对应一条边,从一个点流出,向另一个点流入。这样,对于等式右边的常数C,如果是正的,对应从源点向该点连一条流量C,费用0的边;如果是负的对应从该点向汇点连一条流量-C,费用0的边。对于每个变量,从它系数为正的式子向系数为负的式子连一条容量为+oo,费用为它在目标函数里系数的边。这样我们的网络流模型就构造完毕了。
用spfa暴力增广可以得到70分(后面的点会超时),如果用更快一点的原始对偶或者zkw是可以得到满分的。这里只说说zkw。由于本人技艺不精,一开始不知道zkw对于这种有负边的图需要特殊处理,所以一直没搞出来,后来经过高人指点,知道这种情况需要跑一边spfa初始化距离标号。在这个题中由于是最大费用流,那么距离标号的意义就是到汇点的最大距离,于是从汇点开始沿着反向边跑一次最短路就可以了。
献上代码:
单纯形
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define maxn 1010 7 #define maxm 10010 8 #define inf 2147483647 9 using namespace std; 10 int a[maxn][maxm],next[maxm]; 11 int n,m; 12 13 void pivot(int l,int e) 14 { 15 int last=-1; 16 for (int i=0;i<=m;i++) 17 if (a[l][i]) 18 { 19 next[i]=last; 20 last=i; 21 } 22 for (int i=0;i<=n;i++) 23 { 24 if (a[i][e]==0||i==l) continue; 25 for (int j=last;j!=-1;j=next[j]) 26 { 27 //cout<<j<<' '; 28 if (j==e) continue; 29 a[i][j]-=a[i][e]*a[l][j]; 30 } 31 //cout<<endl; 32 a[i][e]=-a[i][e]; 33 } 34 } 35 36 int simplex() 37 { 38 while (1) 39 { 40 int now=0; 41 for (int i=1;i<=m;i++) 42 if (a[0][i]>0) { now=i; break; } 43 if (now==0) return -a[0][0]; 44 int tmp,mi=inf; 45 for (int i=1;i<=n;i++) 46 { 47 if (a[i][now]>0&&a[i][0]<mi) 48 { 49 tmp=i; 50 mi=a[i][0]; 51 } 52 } 53 pivot(tmp,now); 54 } 55 } 56 57 int main() 58 { 59 //freopen("defend.in","r",stdin); 60 scanf("%d%d",&n,&m); 61 for (int i=1;i<=n;i++) scanf("%d",&a[i][0]); 62 int x,y; 63 for (int i=1;i<=m;i++) 64 { 65 scanf("%d%d%d",&x,&y,&a[0][i]); 66 for (int j=x;j<=y;j++) 67 a[j][i]=1; 68 } 69 int ans=simplex(); 70 printf("%d\n",ans); 71 return 0; 72 }
zkw
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define maxn 1010 7 #define maxm 200000 8 #define inf 2147483647 9 using namespace std; 10 struct et 11 { 12 int s,t,val,cost,next; 13 }e[maxm]; 14 int fir[maxn],a[maxn],dis[maxn],v[maxn],q[maxm],pre[maxn]; 15 bool inque[maxn]; 16 int n,m,st,ed,tot,ans; 17 18 void prepare() 19 { 20 for (int i=st;i<=ed;i++) dis[i]=-inf; 21 int head=0,tail=1; 22 q[1]=ed; dis[ed]=0; inque[ed]=1; 23 while (head<tail) 24 { 25 int now=q[++head]; 26 for (int j=fir[now];j;j=e[j].next) 27 { 28 int k=e[j].t; 29 if (e[j^1].val&&dis[k]<dis[now]+e[j^1].cost) 30 { 31 dis[k]=dis[now]+e[j^1].cost; 32 if (!inque[k]) q[++tail]=k,inque[k]=1; 33 } 34 } 35 inque[now]=0; 36 } 37 } 38 39 int dfs(int now,int flow) 40 { 41 if (now==ed) 42 { 43 ans+=dis[st]*flow; 44 return flow; 45 } 46 int sap=0; v[now]=1; 47 for (int j=fir[now];j;j=e[j].next) 48 { 49 int k=e[j].t; 50 if (!v[k]&&e[j].val&&dis[now]==dis[k]+e[j].cost) 51 { 52 int tmp=dfs(k,min(e[j].val,flow-sap)); 53 e[j].val-=tmp; 54 e[j^1].val+=tmp; 55 sap+=tmp; 56 if (sap==flow) return sap; 57 } 58 } 59 return sap; 60 } 61 62 bool adjust() 63 { 64 int tmp=-inf; 65 for (int i=st;i<=ed;i++) if (v[i]) 66 for (int j=fir[i];j;j=e[j].next) 67 { 68 int k=e[j].t; 69 if (!v[k]&&e[j].val) tmp=max(tmp,dis[k]+e[j].cost-dis[i]); 70 } 71 if (tmp==-inf) return 0; 72 for (int i=st;i<=ed;i++) if (v[i]) 73 dis[i]+=tmp; 74 return 1; 75 } 76 77 void add(int x,int y,int z,int w) 78 { 79 e[++tot].s=x; e[tot].t=y; e[tot].val=z; e[tot].cost=w; e[tot].next=fir[x]; fir[x]=tot; 80 e[++tot].s=y; e[tot].t=x; e[tot].val=0; e[tot].cost=-w; e[tot].next=fir[y]; fir[y]=tot; 81 } 82 83 int main() 84 { 85 //freopen("defend.in","r",stdin); 86 scanf("%d%d",&n,&m); 87 st=0; ed=n+2; tot=1; 88 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 89 for (int i=1;i<=n+1;i++) 90 if (a[i]-a[i-1]>0) add(st,i,a[i]-a[i-1],0); 91 else add(i,ed,a[i-1]-a[i],0); 92 int x,y,z; 93 for (int i=1;i<=m;i++) 94 { 95 scanf("%d%d%d",&x,&y,&z); 96 add(x,y+1,inf,z); 97 } 98 for (int i=1;i<=n;i++) 99 add(i,i+1,inf,0); 100 prepare(); 101 //for (int i=st;i<=ed;i++) cout<<dis[i]<<' '; cout<<endl; 102 do 103 do memset(v,0,sizeof(v)); 104 while (dfs(st,inf)); 105 while (adjust()); 106 printf("%d\n",ans); 107 return 0; 108 }