Bzoj2673 3961: [WF2011]Chips Challenge 費用流


國際慣例題面:

如果我們枚舉放幾個零件的話,第二個限制很容易解決,但是第一個怎么辦?(好的,這么建圖不可做)
考慮我們枚舉每行每列最多放幾個零件t,然后計算零件總數sum。這樣如果可行的話,則有t*B<=sum*A。
考慮第一個限制怎么辦。我們可以欽定所有可行的位置都放上零件,然后再把多的零件拆下來。
我們令sxi為第i行能放的最多零件數,syi為第i列能放的最多零件數。
由源點向每一行連流量sxi費用0的邊,每一列向匯點連流量syi費用0的邊。
然后讓每一行i向每一列i連流量t費用0的邊,表示第i行和第j列最多同時不拆(留下)t個零件。
最后對於所有輸入為'.'的格子ij,由第i行向第j列連容量1費用1的邊,表示拆下第i行第j列的零件。
跑費用流,必須滿流才滿足題意(零件要么行和列同時留下,要么拆掉),費用表示拆掉的零件個數。
然后用留下的零件個數和t去比較是否合法,計算答案就行了。
感覺這題就是給了我們一種網絡流建圖,補集轉化的姿勢(如果選擇的流量行列不移動統一,那么去掉的流量是否統一?如果選擇的不好限制,去掉的是否容易限制?)

代碼:

 1 #pragma GCC optimize(2)
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 const int maxn=1e2+1e1,maxe=maxn*maxn;
 7 const int inf=0x3f3f3f3f;
 8 
 9 char in[maxn][maxn];
10 int s[maxn],t[maxe<<3],nxt[maxe<<3],f[maxe<<3],c[maxe<<3],cnt;
11 int dis[maxn],inq[maxn],ins[maxn],st,ed,cst;
12 int sx[maxn],sy[maxn],su,ini;
13 int n,A,B,ans;
14 
15 inline void coredge(int from,int to,int flow,int cost) {
16     t[++cnt] = to , f[cnt] = flow , c[cnt] = cost , 
17     nxt[cnt] = s[from] , s[from] = cnt;
18 }
19 inline void singledge(int from,int to,int flow,int cost) {
20     coredge(from,to,flow,cost) , coredge(to,from,0,-cost);
21 }
22 inline bool spfa() {
23     memset(dis,0x3f,sizeof(dis)) , dis[st] = 0;
24     std::queue<int> q; q.push(st) , inq[st] = 1;
25     while( q.size() ) {
26         const int pos = q.front(); q.pop() , inq[pos] = 0;
27         for(int at=s[pos];at;at=nxt[at])
28             if( f[at] && dis[t[at]] > dis[pos] + c[at] ) {
29                 dis[t[at]] = dis[pos] + c[at];
30                 if( !inq[t[at]] ) q.push(t[at]) , inq[t[at]] = 1;
31             }
32     }
33     return dis[ed] != inf;
34 }
35 inline int dfs(int pos,int flow) {
36     if( pos == ed ) return flow;
37     int ret = 0 , now = 0; ins[pos] = 1;
38     for(int at=s[pos];at;at=nxt[at])
39         if( f[at] && !ins[t[at]] && dis[t[at]] == dis[pos] + c[at] ) {
40             now = dfs(t[at],std::min(flow,f[at])) ,
41             ret += now , flow -= now , cst += c[at] * now ,
42             f[at] -= now , f[at^1] += now;
43             if( !flow ) return ins[pos] = 0 , ret;
44         }
45     if( !ret ) dis[pos] = inf;
46     return ins[pos] = 0 , ret;
47 }
48 inline int flow() { // we must got full flow .
49     int ret = 0 , now = 0;
50     while( spfa() ) while( ( now = dfs(st,inf) ) ) ret += now;
51     return ret;
52 }
53 
54 inline void build(int t) {
55     memset(s,0,sizeof(s)) , cnt = 1 , st = n * 2 + 1 , ed = st + 1 , cst = 0;
56     #define cov(x,id) (x*2-1+id)
57     for(int i=1;i<=n;i++) {
58         singledge(st,cov(i,0),sx[i],0) ,
59         singledge(cov(i,0),cov(i,1),t,0) ,
60         singledge(cov(i,1),ed,sy[i],0) ;
61     }
62     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if( in[i][j] == '.' ) singledge(cov(i,0),cov(j,1),1,1);
63 }
64 inline int calc(int t) {
65     build(t);
66     if( flow() != su ) return -1; // illegal .
67     int used = su - cst;
68     return used * A >= t * B ? used - ini : -1;
69 }
70 
71 int main() {
72     static int cse;
73     while( scanf("%d%d%d",&n,&A,&B) == 3 && ( n || A || B ) ) {
74         memset(sx,0,sizeof(sx)) , memset(sy,0,sizeof(sy)) , ini = su = 0 , ans = -1;
75         for(int i=1;i<=n;i++) {
76             scanf("%s",in[i]+1);
77             for(int j=1;j<=n;j++){
78                 if( in[i][j] != '/' ) ++sx[i] , ++sy[j] , ++su;
79                 ini += in[i][j] == 'C';
80             }
81         }
82         for(int i=0;i<=n;i++) ans = std::max( ans , calc(i) );
83         printf("Case %d: ",++cse);
84         if( !~ans ) puts("impossible");
85         else printf("%d\n",ans);
86     }
87     return 0;
88 }
View Code


(話說我都多路增廣+碼內O2了才卡到332MS,那幾個幾十MS過掉的是怎么做到的)

華やかな 煌びやかな運命を
做着擁有絢爛命運的夢
夢見て 泣いた夜は
卻因為這樣的夢 止不住淚水
銀色の流星も泣いている
銀色的流星也在哭泣
ナツシスに向けて
向着水仙花海
あっけなく さよならを告げぬよう
不想輕易說出再見
唇が 告げぬように
所以咬緊雙唇
呟きに星空も木霊する
唯有輕聲細語回響在星空下
ナツシスに向けて
朝着水仙
いつまでも屆け
將它傳遞到永遠


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM