转载: http://blog.csdn.net/axuan_k/article/details/47297395
题目描述:
现在要针对多赛区竞赛制定一个预算,该预算是一个行代表不同种类支出、列代表不同赛区支出的矩阵。组委会曾经开会讨论过各类支出的总和,以及各赛区所需支出的总和。
另外,组委会还讨论了一些特殊的约束条件:例如,有人提出计算机中心至少需要1000K 里亚尔(伊朗货币),用于购买食物;也有人提出Sharif 赛区用于购买体恤衫的费用不能超过30000K 里亚尔。
组委会的任务是制定一个满足所有约束条件且行列和满足要求的预算。
解题报告:
该题解题关键在于建图, 将行置于左边,列置于右边,然后连接所有行和列,每条边表示第x行第y列的物品的价格预算。
根据约束条件来限定这些边的上界与下界,从而将题目转化为 有上下界的可行流求解:
已有边的权为c[i][j]-b[i][j]
另外构造源点和汇点 分别连接 所有行 和 所有列
边权为 sum[x]-sum(b[x][i]) sum[y]-sum(b[i][y])
如果源流和汇流相等 则说明有可行解
相当于无源,我们只需汇点向源点连一条[0,inf]的边就行了
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <queue> 6 const int MAXN =505; 7 const int MAXM=440020; 8 const int INF=0x3f3f3f; 9 const int maxx=0x3f3f3f3f; 10 using namespace std; 11 struct Edge { 12 int to,cap,flow,next; 13 } edge[MAXM]; 14 int head[MAXN],tot,gap[MAXN],d[MAXN],cur[MAXN],que[MAXN],p[MAXN]; 15 16 void init() 17 { 18 tot=0; 19 memset(head,-1,sizeof(head)); 20 } 21 22 int isap(int source,int sink,int N) 23 { 24 memset(gap,0,sizeof(gap)); 25 memset(d,0,sizeof(d)); 26 memcpy(cur,head,sizeof(head)); 27 int top = 0,x = source,flow = 0; 28 while(d[source] < N) { 29 if(x == sink) { 30 int Min = maxx,inser=0; 31 for(int i = 0; i < top; ++i) { 32 if(Min > edge[p[i]].cap - edge[p[i]].flow) { 33 Min = edge[p[i]].cap - edge[p[i]].flow; 34 inser = i; 35 } 36 } 37 for(int i = 0; i < top; ++i) { 38 edge[p[i]].flow += Min; 39 edge[p[i]^1].flow -= Min; 40 } 41 if(Min!=INF) flow += Min; 42 top = inser; 43 x = edge[p[top]^1].to; 44 continue; 45 } 46 int ok = 0; 47 for(int i = cur[x]; i != -1; i = edge[i].next) { 48 int v = edge[i].to; 49 if(edge[i].cap > edge[i].flow && d[v]+1 == d[x]) { 50 ok = 1; 51 cur[x] = i; 52 p[top++] = i; 53 x = edge[i].to; 54 break; 55 } 56 } 57 if(!ok) { 58 int Min = N; 59 for(int i = head[x]; i != -1; i = edge[i].next) { 60 if(edge[i].cap > edge[i].flow && d[edge[i].to] < Min) { 61 Min = d[edge[i].to]; 62 cur[x] = i; 63 } 64 } 65 if(--gap[d[x]] == 0) break; 66 gap[d[x] = Min+1]++; 67 if(x != source) x = edge[p[--top]^1].to; 68 } 69 } 70 return flow; 71 } 72 73 int b[205][25],h[205][25]; 74 int sumr[205],sumc[205]; 75 int id[205][25]; //用来保存[i][j]在邻接表的边的编号 76 int s,t; 77 int n,m; 78 int sum; 79 int flag; 80 81 void addedge(int u,int v,int c,int x,int y) 82 { 83 edge[tot]=(Edge){v,c,0,head[u]}; 84 id[x][y]= head[u] = tot++; 85 edge[tot]=(Edge){u,c,c,head[v]}; 86 head[v] = tot++; 87 } 88 89 void set_(int x,int y,char ch,int v) 90 { 91 if(ch=='=') 92 { 93 if(b[x][y]>v) 94 flag=1; 95 if(h[x][y]<v) 96 flag=1; 97 b[x][y]=h[x][y]=v; 98 } 99 else if(ch=='>') 100 { 101 if(h[x][y]<=v) 102 flag=1; 103 b[x][y]=max(b[x][y],v+1); 104 } 105 else 106 { 107 if(b[x][y]>=v) 108 flag=1; 109 h[x][y]=min(h[x][y],v-1); 110 } 111 } 112 113 void build() 114 { 115 for(int i=1;i<=n;i++) 116 for(int j=1;j<=m;j++) 117 addedge(i,n+j,h[i][j]-b[i][j],i,j); 118 for(int i=1;i<=n+m;i++) 119 { 120 int f=i<=n?sumr[i]:-sumc[i-n]; 121 if(i<=n) 122 for(int j=1;j<=m;j++) 123 f-=b[i][j]; 124 else 125 for(int j=1;j<=n;j++) 126 f+=b[j][i-n]; 127 if(f>0){ 128 addedge(s,i,f,0,0); 129 sum+=f; 130 } 131 else 132 addedge(i,t,-f,0,0); 133 } 134 } 135 136 int main() 137 { 138 // freopen("in.txt","r",stdin); 139 int T; 140 int nn,a,bb,c; 141 char ch; 142 scanf("%d",&T); 143 for(int Case=1;Case<=T;Case++) 144 { 145 if(Case!=1) 146 printf("\n"); 147 scanf("%d%d",&n,&m); 148 init(); 149 s=0,t=n+m+1; 150 sum=0; 151 flag=0; 152 153 for(int i=1;i<=n;i++) 154 for(int j=1;j<=m;j++) //初始上下界 155 { 156 b[i][j]=0; 157 h[i][j]=INF; 158 } 159 for(int i=1;i<=n;i++) 160 scanf("%d",&sumr[i]); 161 for(int j=1;j<=m;j++) 162 scanf("%d",&sumc[j]); 163 scanf("%d",&nn); 164 while(nn--) //限定上下界 165 { 166 cin>>a>>bb>>ch>>c; 167 if(!a&&!bb) 168 { 169 for(int i=1;i<=n;i++) 170 for(int j=1;j<=m;j++) 171 set_(i,j,ch,c); 172 } 173 else if(!a&&bb) 174 { 175 for(int i=1;i<=n;i++) 176 set_(i,bb,ch,c); 177 } 178 else if(a&&!bb) 179 { 180 for(int i=1;i<=m;i++) 181 set_(a,i,ch,c); 182 } 183 else 184 set_(a,bb,ch,c); 185 } 186 if(flag==1){ //约束条件矛盾 187 printf("IMPOSSIBLE\n"); 188 continue; 189 } 190 build(); 191 int ans=isap(s,t,t+1); 192 193 if(sum!=ans) //源流==汇流 194 printf("IMPOSSIBLE\n"); 195 else { 196 for(int i=1;i<=n;i++) 197 for(int j=1;j<=m;j++) 198 if(j==m) 199 printf("%d\n",b[i][j]+edge[id[i][j]].flow); 200 else 201 printf("%d ",b[i][j]+edge[id[i][j]].flow); 202 } 203 } 204 return 0; 205 }