ディレクトリ
詳細高度なネットワークフローアルゴリズム
基本
書かれた要約する前に、この章では、比較的単純で書かれているので
最大の流れ
モデルは、多くのチューブ、ソースポイントからINF流出ストリームがあること、聞いているどのくらいのトラフィックミーティングポイント
1つの明白なアイデアは貪欲ですが、反例を与えることは容易です。
あなたが達成できるように、そして、(適切にこれを題し?それとも植樹と呼ばれる)の後に植物の木と同様の問題は、その質問には、それは、それのために投票しないだろう重量と次の選挙を否定する点を取るために貪欲マージポイントです禁反言効果
その後、我々はそれぞれの発信トラフィックの後、裏面を確立し、プラスの流れの裏側に相当するので、私たちは貪欲の正しさを保証することができます。
辺の重みは、最小流れループを実行することは、負のネガ可能確保する場合
最小割
あなたは3つの結論をライセンスする必要がある=最大フローは、ありませんが、証拠がブログを書く前にあり、ここに書いていない逆命題であります
実際には、(ライン上で覚えて
EKアルゴリズム
各時間は、上のトラフィックの流れを増大さと離れてシンクにソースマイナスのトラフィックフローによってエッジセットを置くことができる方法を見つけます
DINICアルゴリズム
複数の増補、より良いアルゴリズムがありますが、このアルゴリズムは、比較的一般的ですが、タイトルはカードではないでしょう
BFSは、層状の最初の具体的な実現では、DFSは流れ去るすべてのトラフィックは、達成するために、コードを見て、この部分のニーズを流れることができます。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
const int N=201,M=10001;
struct node{
int v,next;ll w;
}edge[M*2];
int top=1,head[N],cur[N];
const ll inf=100909260817ll;
inline void add(int from,int to,int w){
edge[++top].v=to;
edge[top].next=head[from];
head[from]=top;
edge[top].w=w;
}
inline int read(){
char ch=getchar();int x=0;int pos=1;
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return pos?x:-x;
}
int n,m,s,t;
int dir=0,level[N];
queue<int>q;
inline int Min(ll a,ll b){
return a>b?b:a;
}
inline int bfs(){
memset(level,-1,sizeof(level));
level[s]=0;q.push(s);
while(!q.empty()){
int now=q.front();q.pop();
for(int i=head[now];i;i=edge[i].next){
int v=edge[i].v;
if(level[v]==-1&&edge[i].w){
level[v]=level[now]+1;
q.push(v);
}
}
}
if(level[t]==-1) return 0;
else return 1;
}
inline ll dfs(int now,ll flow){
if(now==t) return flow;
ll res=flow;
for(int &i=cur[now];i;i=edge[i].next){
int v=edge[i].v;
if(edge[i].w&&level[v]==level[now]+1){
ll k=dfs(v,Min(edge[i].w,res));
res-=k;edge[i].w-=k;edge[i^1].w+=k;
}
if(!res) break;
}
return flow-res;
}
inline ll dinic(){
ll ans=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
ans+=dfs(s,inf);
}
return ans;
}
int main(){
n=read();m=read();s=read();t=read();
for(int i=1;i<=m;i++){
int ui=read(),vi=read(),wi=read();
add(ui,vi,wi);
add(vi,ui,0);
}
printf("%lld",dinic());
return 0;
}
二部グラフの複雑さ
\(M *分(N ^ \ FRAC {2} {3}、M ^ \ FRAC {1} {2})\)
最小費用フロー
このとき、各チューブの流れはコストがかかります。で、最大流量の場合、確かにライン上のアクセスDINICの順序を変更するので、この時点で、小さい側を取るために優先権があります。これは、それを達成するためのコードを見て、(右は負の側面を有していてもよく、使用Dijとすることはできません)spfaを置き換えBFSに行われます
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<ctime>
#define ll long long
#include<cstdlib>
#include<queue>
using namespace std;
const int N = 401,M=15001;
struct node{
int u,v,c,w,nex;
}edge[M<<1];
int head[N],top=1;
const int inf = 1926081700;
inline void add(int u,int v,int w,int c){
edge[++top].v=v;edge[top].w=w;edge[top].c=c;edge[top].nex=head[u];head[u]=top;
}
inline int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
int n,m,maxflow,mincost,dis[N],vis[N],cur[N];
namespace DINIC{
int s,t;
int SPFA(){
queue<int>q; q.push(s);
for(int i=1;i<=n;i++) dis[i]=inf;
dis[s]=0;
while(!q.empty()){
int now=q.front();q.pop();
vis[now]=0;
for(int i=head[now];i;i=edge[i].nex){
int v=edge[i].v;
if(edge[i].w&&dis[now]+edge[i].c<dis[v]){
dis[v]=dis[now]+edge[i].c;
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
return dis[t]==inf?0:1;
}
int dfs(int now,int flow){
if(now==t||flow==0) return flow;
int res=flow;vis[now]=1;
for(int &i=cur[now];i;i=edge[i].nex){
int v=edge[i].v;
if(!vis[v]&&edge[i].w&&dis[v]==dis[now]+edge[i].c){
int nw=dfs(v,min(res,edge[i].w));
res-=nw,edge[i].w-=nw,edge[i^1].w+=nw,mincost+=edge[i].c*nw;if(!res) break;
}
}
vis[now]=0;
return flow-res;
}
void solve(){
while(SPFA()){
memcpy(cur,head,sizeof(head));
maxflow+=dfs(s,inf);
}
}
};
using namespace DINIC;
int main(){
n=read(),m=read();s=1;t=n;
for(int i=1,u,v,c,w;i<=m;i++){
u=read(),v=read(),w=read(),c=read();
add(u,v,w,c);
add(v,u,0,-c);
}
solve();
printf("%d %d",maxflow,mincost);
return 0;
}
高度
2020qbxt地方の選挙クラスが(話していたので、メインスピーカーの前で他の人々への配慮のレベル
パッシブ交換を実現可能な流れ
アルゴリズムのアイデア:
各エッジ最初の強制流は上限下限各エッジ保存に割り当てられた重みであろう流れを、境界、及びそれぞれの記録がより多くまたはポリ流れる流出ドット(メモリ・アレイは、\(totflowは\)を表し流入マイナストラフィックフローの流出、ケースの不均衡、トラフィックを分散する必要性
スーパーの新しいソースとシンク\(S、T \)
\(totflow_i <0 \) 、複数の流入プルーフ、その他の点の流れが必要、この時間\(S \)に\(I \)が接続された( - totflow \)\エッジ
それ以外の場合、この時間、それがアートワークを流れる作るために追加のポイントを必要とし、マルチ流れる証明に(私は\)\に\(T \)でも\(totflow \)側
体重と正の重みポイント記録中\(\ SUM) (重量点とこの点は、正の重みと重量=負の重みであり、上記のようにプラス(W \)\それが減算される(W \)を\)
\(S \)に\(T \) 、実行\(Dinic \) 、最大流量場合=あなたが完全にアンバランスを補償することができる合計、実現可能な流れがあり、各エッジに比べて流れ
下界容量+ - +フロー=が逆流側の下限
アクティブ交換境界最大流量
同じアルゴリズムは思考であります:
- \(S \)に\(T \)接続容量(\ \ INF \)側、循環流構成
- パッシブヒートシンク実現可能なフロー法の使用が解決するかどうか、注意を払う新しいソースとシンクとシンクの元のソースとの間の差に決定します
- ネットワーク上の残存量から溶液、場合\(S \)に\(T \)ラン\(Dinic \) 、最大流量の回答であります
コード:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
const int inf = 192608170;
using namespace std;
int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
const int N = 441,M=50001;
struct node{
int v,nex,w;
}edge[M];
int head[N],top=1;
int s,t,ns,nt,n,m,totflow[M];
void add(int u,int v,int w){
edge[++top].w=w;edge[top].nex=head[u];edge[top].v=v;head[u]=top;
edge[++top].w=0;edge[top].nex=head[v];edge[top].v=u;head[v]=top;
}
int level[N],cur[N];
int bfs(){
queue<int>q;
memset(level,-1,sizeof(level));
q.push(s);level[s]=0;
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=head[now];i;i=edge[i].nex){
int v=edge[i].v;
if(level[v]==-1&&edge[i].w){
level[v]=level[now]+1;
q.push(v);
}
}
}
return level[t]!=-1;
}
int dfs(int now,int flow){
int res=flow;
if(now==t||flow==0) return flow;
for(int &i=cur[now];i;i=edge[i].nex){
int v=edge[i].v;
if(level[v]==level[now]+1&&edge[i].w){
int nw=dfs(v,min(edge[i].w,res));
res-=nw;edge[i].w-=nw;edge[i^1].w+=nw;
if(!res) break;
}
}
return flow-res;
}
int dinic(int S,int T){
s=S,t=T;
int ans=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
ans+=dfs(s,inf);
}
return ans;
}
int las[N];
void del(int now){
edge[now].w=0;
}
int main(){
n=read(),m=read(),ns=read(),nt=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),low=read(),up=read();
add(u,v,up-low);
totflow[u]-=low;totflow[v]+=low;
}
int num=top;
int S=n+1,T=n+2,sum=0;
for(int i=1;i<=n;i++){
if(totflow[i]<0) add(i,T,-totflow[i]);
else{
add(S,i,totflow[i]);
sum+=totflow[i];
}
las[i]=top;
}
add(nt,ns,inf);
int ans=0;
if(dinic(S,T)!=sum){
printf("please go home to sleep");return 0;
}else{
ans=edge[top].w;
del(top);del(top-1);
ans+=dinic(ns,nt);
printf("%d",ans);
}
return 0;
}
アクティブシンク境界最小流量
同様に、実現可能なフローに基づいて、我々は、総流量を最小限にしたいです
次にから実用的に最大流量を減算\(T \)に\(S \)フロー、最大流量は、実行することができ
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
const ll inf = 19260817000000ll;
using namespace std;
ll read(){
ll x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
const ll N = 100031,M=400001;
struct node{
ll v,nex,w;
}edge[M];
ll head[N],top=1;
ll s,t,ns,nt,n,m,totflow[M];
void add(ll u,ll v,ll w){
edge[++top].w=w;edge[top].nex=head[u];edge[top].v=v;head[u]=top;
edge[++top].w=0;edge[top].nex=head[v];edge[top].v=u;head[v]=top;
}
ll level[N],cur[N];
ll bfs(){
queue<ll>q;
memset(level,-1,sizeof(level));
q.push(s);level[s]=0;
while(!q.empty()){
ll now=q.front();
q.pop();
for(ll i=head[now];i;i=edge[i].nex){
ll v=edge[i].v;
if(level[v]==-1&&edge[i].w){
level[v]=level[now]+1;
q.push(v);
}
}
}
return level[t]!=-1;
}
ll dfs(ll now,ll flow){
ll res=flow;
if(now==t||flow==0) return flow;
for(ll &i=cur[now];i;i=edge[i].nex){
ll v=edge[i].v;
if(level[v]==level[now]+1&&edge[i].w){
ll nw=dfs(v,min(edge[i].w,res));
res-=nw;edge[i].w-=nw;edge[i^1].w+=nw;
if(!res) break;
}
}
return flow-res;
}
ll dinic(ll S,ll T){
s=S,t=T;
ll ans=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
ans+=dfs(s,inf);
}
return ans;
}
ll las[N];
void del(ll now){
edge[now].w=0;
}
int main(){
n=read(),m=read(),ns=read(),nt=read();
for(ll i=1;i<=m;i++){
ll u=read(),v=read(),low=read(),up=read();
add(u,v,up-low);
totflow[u]-=low;totflow[v]+=low;
}
ll num=top;
ll S=n+1,T=n+2,sum=0;
for(ll i=1;i<=n;i++){
if(totflow[i]<0) add(i,T,-totflow[i]);
else{
add(S,i,totflow[i]);
sum+=totflow[i];
}
las[i]=top;
}
add(nt,ns,inf);
ll ans=0;
if(dinic(S,T)!=sum){
printf("please go home to sleep");return 0;
}else{
ans=edge[top].w;
del(top);del(top-1);
ans-=dinic(nt,ns);
printf("%lld",ans);
}
return 0;
}
いくつかのビルドグラフィカルモデル
最大重量部分グラフを閉じました
新しいネットワーク・フロー・ダイアグラムの構築を検討し、ソースとシンクを追加します\(S \) 、(T \)\
- すべての正の側に右の点からSでも、流動点の右側
- すべての点であっても負側、右の点の絶対値の流量に右からT
- 原画像内のすべてのエッジは、新しいイメージに接続され、流量無限
- 答えは、「オリジナルの蒋介石の権利及び」マイナス「最小カット新マップ」であります
なぜこれを行いますか?
我々は決断を置くために持っているもの、正のポイント数を選択する権利がポイントに負の権利の一部を選択したい場合があります検討し、我々はこれらの点のために投票しない決定を選択する必要があり、このことは、最小の処理にカットすることができ
- 、右は明らかにも必要とされない切削エッジの各辺のうち、負の右点で左側が正正しいスポットであるように、二部グラフを検討
- 減算されることになって、何カット権利は、選択された点は、右図の閉鎖の右点から選択された発現の負のネガをカットする権利である表していません。
- カット性を保証\(S \)及び(T \)\、選挙は、それが右側にカット負重量でなければならない点(左側に切断されていない)の右側で連通していないが、選択された負の右点が接続されている基を表し、保証これは、閉じた図形、またはその逆であります
- 判定:あなたが左にポイントカットを選択して、彼らはこの点を選ぶだけでなく、負の右の後継者、そうでない場合、我々は、最適な意思決定の方式の最小カットアルゴリズムと、選んだのではないと言ったことができます
例
文理分科
裸TOO
寿司レストラン
負の値に設定別のコスト、
美味しさは次のように、正の値を取得し、図は、以下の組み込みの(仮定(= 2 A_1、A_2 = 3、A_3 = 2 \)\(\ (mは* 1 * 1 \)コストの一種であり、X 1はすべきではありません、注目を集めていない場合))
その上で最大のクローズド部分グラフを実行する権利
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
const int N = 6001,M=50001;
const int inf = 192608170;
int n,m,id[N],idc[N],a[N],d[201][201],wd[201][201],s,t,cnt=0,aed[N];
int head[N],cur[N],top=1;
struct node{
int v,nex,w;
}edge[M*2];
inline void add(int u,int v,int w){
edge[++top].v=v;edge[top].nex=head[u];edge[top].w=w;head[u]=top;
edge[++top].v=u;edge[top].nex=head[v];edge[top].w=0;head[v]=top;
}
int vis[N];
inline int bfs(){
memset(vis,-1,sizeof(vis));
queue<int>q;
q.push(s);vis[s]=0;
while(!q.empty()){
int now=q.front();q.pop();
for(register int i=head[now];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]==-1&&edge[i].w){
vis[v]=vis[now]+1;
q.push(v);
}
}
}
return vis[t]!=-1;
}
inline int dfs(int now,int flow){
int res=flow;
if(now==t||!flow) return flow;
for(int &i=cur[now];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]==vis[now]+1&&edge[i].w){
int nw=dfs(v,min(res,edge[i].w));
edge[i].w-=nw;edge[i^1].w+=nw;res-=nw;
if(!res) break;
}
}
return flow-res;
}
inline int dinic(){
int res=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
res+=dfs(s,inf);
}
return res;
}
int main(){
n=read(),m=read();
int tot=0;
// printf("%d %d\n",cnt,top);
for(register int i=1;i<=n;++i){
a[i]=read();
if(!id[a[i]]) id[a[i]]=++cnt;
}
for(register int i=1;i<=n;++i) idc[i]=++cnt;
// printf("%d %d\n",cnt,top);
for(register int i=1;i<=n;++i){
for(register int j=1;j<=n-i+1;++j){
wd[i][i+j-1]=read();d[i][i+j-1]=++cnt;
}
}
s=++cnt;
t=++cnt;
// printf("%d %d\n",cnt,top);
for(register int len=n;len>=1;--len){
for(register int l=1;l<=n-len+1;++l){
int r=l+len-1;
if(l!=r){
add(d[l][r],d[l+1][r],inf);
add(d[l][r],d[l][r-1],inf);
}else{
add(d[l][r],idc[l],inf);
add(idc[l],t,a[l]);
add(d[l][r],id[a[l]],inf);
}
if(wd[l][r]>0) add(s,d[l][r],wd[l][r]),tot+=wd[l][r];
else if(wd[l][r]<0) add(d[l][r],t,-wd[l][r]);
}
}
// printf("%d %d\n",cnt,top);
for(register int i=1;i<=n;++i){
if(!aed[a[i]]){
add(id[a[i]],t,m*a[i]*a[i]);
aed[a[i]]=1;
}
}
// printf("%d %d\n",cnt,top);
/* for(int i=1;i<=cnt;i++){
for(int j=head[i];j;j=edge[j].nex){
int v=edge[j].v;
if(edge[j].w){
printf("%d %d %d\n",i,v,edge[j].w);
}
}
}*/
// printf("%d %d ed\n",cnt,top);
printf("%d",tot-dinic());
return 0;
}
宇宙戦争
二部+ dinic、Dinicは実現可能性を決定するために、ここで使用され、コストの流れは上記の行きたくありません
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
const double eps = 1e-4;
#define ll unsigned long long
using namespace std;
const int N=201,M=10001;
struct node{
int v,next;ll w;
}edge[M*2];
int top=1,head[N],cur[N];
const ll inf=1009092608170000ll;
inline void add(int from,int to,ll w){
edge[++top].v=to;edge[top].next=head[from];head[from]=top;edge[top].w=w;
edge[++top].v=from;edge[top].next=head[to];head[to]=top;edge[top].w=0;
}
inline int read(){
char ch=getchar();int x=0;int pos=1;
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return pos?x:-x;
}
int n,m,s,t;
int dir=0,level[N];
queue<int>q;
inline ll Min(ll a,ll b){
return a>b?b:a;
}
inline int bfs(){
memset(level,-1,sizeof(level));
level[s]=0;q.push(s);
while(!q.empty()){
int now=q.front();q.pop();
for(int i=head[now];i;i=edge[i].next){
int v=edge[i].v;
if(level[v]==-1&&edge[i].w){
level[v]=level[now]+1;
q.push(v);
}
}
}
if(level[t]==-1) return 0;
else return 1;
}
inline ll dfs(int now,ll flow){
if(now==t) return flow;
ll res=flow;
for(int &i=cur[now];i;i=edge[i].next){
int v=edge[i].v;
if(edge[i].w&&level[v]==level[now]+1){
ll k=dfs(v,Min(edge[i].w,res));
res-=k;edge[i].w-=k;edge[i^1].w+=k;
}
if(!res) break;
}
return flow-res;
}
inline ll dinic(){
ll ans=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
ans+=dfs(s,inf);
}
return ans;
}
ll a[N],b[N],tot;
int mp[N][N];
int check(double now){
s=n+m+1,t=n+m+2;
memset(head,0,sizeof(head));
for(int i=1;i<=top;i++){
edge[i].v=edge[i].next=edge[i].w=0;
}
top=1;
for(int i=1;i<=n;i++){
add(i+m,t,a[i]);
}
for(int i=1;i<=m;i++){
add(s,i,(ll)(now*b[i]));
for(int j=1;j<=n;j++){
if(mp[i][j]) add(i,j+m,inf);
}
}
if(dinic()==tot){
return 1;
}else return 0;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++){
a[i]=read();a[i]*=1000ll;tot+=a[i];
}
for(int i=1;i<=m;i++){
b[i]=read();b[i]*=1000ll;
}
double l=0,r=0;
/*for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
}
}*/
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
mp[i][j]=read();
if(mp[i][j]) r+=(double)(a[j]/b[i]);
}
}
while(l<r-eps){
double mid=(l+r)/2.0;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.6f\n",r);
return 0;
}
異なるディアンがある方法で問題の解決策を拡大
poj1149
図の実施例の構成を変更する:考慮豚STY流動点はO(nm)であり、考え点間の人々の流れは、O(N)であるの
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
const int N = 1005,M=2000001;
const int inf = 192608170;
int n,m,s,t;
int head[N],cur[N],top=1;
struct node{
int v,nex,w;
}edge[M*2];
inline void add(int u,int v,int w){
edge[++top].v=v;edge[top].nex=head[u];edge[top].w=w;head[u]=top;
edge[++top].v=u;edge[top].nex=head[v];edge[top].w=0;head[v]=top;
}
int vis[N];
inline int bfs(){
memset(vis,-1,sizeof(vis));
queue<int>q;
q.push(s);vis[s]=0;
while(!q.empty()){
int now=q.front();q.pop();
for(register int i=head[now];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]==-1&&edge[i].w){
vis[v]=vis[now]+1;
q.push(v);
}
}
}
return vis[t]!=-1;
}
inline int dfs(int now,int flow){
int res=flow;
if(now==t||!flow) return flow;
for(int &i=cur[now];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]==vis[now]+1&&edge[i].w){
int nw=dfs(v,min(res,edge[i].w));
edge[i].w-=nw;edge[i^1].w+=nw;res-=nw;
if(!res) break;
}
}
return flow-res;
}
inline int dinic(){
int res=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
res+=dfs(s,inf);
}
return res;
}
int cont[N*12],las[N*12];
int main(){
m=read(),n=read();
s=n+1,t=n+2;
for(int i=1;i<=m;i++){
cont[i]=read();
}
for(int i=1;i<=n;i++){
int A=read();
for(int j=1;j<=A;j++){
int k=read();
if(!las[k]){
add(s,i,cont[k]);
las[k]=i;
}else{
add(las[k],i,inf);
las[k]=i;
}
}
int B=read();
add(i,t,B);
}
printf("%d",dinic());
return 0;
}
CQOI2009ダンス
モデルの制約、制限といくつかの点で、ある時点で除去することができない限界点が存在しない、偶数のエッジの数のうち分割中間サイドポイントの容量が上限限界であります
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
const int N = 305,M=2000001;
const int inf = 192608170;
int n,m,s,t,k,mp[N][N];
int head[N],cur[N],top=1;
struct node{
int v,nex,w;
}edge[M*2];
inline void add(int u,int v,int w){
edge[++top].v=v;edge[top].nex=head[u];edge[top].w=w;head[u]=top;
edge[++top].v=u;edge[top].nex=head[v];edge[top].w=0;head[v]=top;
}
int vis[N];
inline int bfs(){
memset(vis,-1,sizeof(vis));
queue<int>q;
q.push(s);vis[s]=0;
while(!q.empty()){
int now=q.front();q.pop();
for(register int i=head[now];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]==-1&&edge[i].w){
vis[v]=vis[now]+1;
q.push(v);
}
}
}
return vis[t]!=-1;
}
inline int dfs(int now,int flow){
int res=flow;
if(now==t||!flow) return flow;
for(int &i=cur[now];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]==vis[now]+1&&edge[i].w){
int nw=dfs(v,min(res,edge[i].w));
edge[i].w-=nw;edge[i^1].w+=nw;res-=nw;
if(!res) break;
}
}
return flow-res;
}
inline int dinic(){
int res=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
res+=dfs(s,inf);
}
return res;
}
int check(int mid){
memset(head,0,sizeof head);
for(int i=1;i<=top;i++){
edge[i].v=edge[i].nex=edge[i].w=0;
}
top=1;
for(int i=1;i<=n;i++){
add(s,i,mid);
add(i+n*3,t,mid);
add(i,i+n,k);
add(i+n*2,i+n*3,k);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(mp[i][j]){
add(i,j+n*3,1);
}else{
add(i+n,j+n*2,1);
}
}
}
if(dinic()==mid*n){
return 1;
}else return 0;
}
char S[N];
int main(){
n=read(),k=read();s=n*4+1,t=n*4+2;
for(int i=1;i<=n;i++){
scanf("%s",S);
for(int j=1;j<=n;j++){
mp[i][j]=S[j-1]=='Y';
}
}
int l=0,r=n+1;
while(l<r-1){
int mid=(l+r)>>1;
if(check(mid)) l=mid;
else r=mid;
}
printf("%d",l);
return 0;
}