朢罗柳
网络流
最小割
有一些比较典的trick:
-
如果每个点只有两种可能(比如说选或不选),那么我们可以定义:与源点相连的代表选,与汇点相连的代表不选,根据这样的定义建图(集合划分模型)
-
每个点的利润有冲突的话,可以将这两个点连一条 \(inf\) 边代表矛盾
-
如果选了 \(i\) 点以后就对其它点的选择有限制,设为 \(j\) 点(有点不能选了),可以通过连边使得 \(i\) 与 \(j\) 都被割去后源点与汇点仍然联通实现 \(i\) 与 \(j\) 不能同时被割去
看题吧
P2774 方格取数问题
题目描述
有一个 m 行 n 列的方格图,每个方格中都有一个正整数。现要从方格中取数,使任意两个数所在方格没有公共边,且取出的数的总和最大,请求出最大的和。
点击查看代码
思路显然,如果选择了$(x,y)$,那就不能选择它的上下左右 $4$ 个点,将一个格点分成两个点,一个与源点相连,一个与汇点连边,矛盾的点对就左连右。讲究看吧。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int X[]={1,-1,0,0},Y[]={0,0,1,-1},inf=1e9;
int n,m,S,T,a[105][105],ans,tot=1,head[(int)2e6+10];
struct edge{int to,nex,w;}e[(int)2e6+100];
int dis[(int)2e6+100];
void Add(int u,int v,int w)
{e[++tot]={v,head[u],w};head[u]=tot;}
inline int id(int x,int y,int tag)
{return (x-1)*n+y+tag*n*m;}
queue<int>q;
bool bfs() {
for(int i=1;i<=T;i++) dis[i]=0;
dis[S]=1;q.push(S);
while(q.size()) {
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nex) {
int v=e[i].to;
if(e[i].w>0&&dis[v]==0) {
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[T]>0;
}
int dfs(int u,int flow) {
if(u==T||flow==0) return flow;
int f=0;
for(int i=head[u];i&&flow;i=e[i].nex) {
int v=e[i].to;
if(e[i].w>0&&dis[v]==dis[u]+1) {
int op=dfs(v,min(flow,e[i].w));
if(op==0) dis[v]=0;
f+=op;flow-=op;
e[i].w-=op;e[i^1].w+=op;
}
}
return f;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>m>>n;S=n*m*2+1;T=n*m*2+2;
for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) cin>>a[i][j];
int sum=0;
for(int i=1;i<=m;i++) {
for(int j=1;j<=n;j++) {
for(int t=0;t<4;t++) {
int tx=i+X[t],ty=j+Y[t];
if(tx<1||tx>m||ty<1||ty>n) continue;
Add(id(i,j,0),id(tx,ty,1),inf);
Add(id(tx,ty,1),id(i,j,0),0);
}
Add(S,id(i,j,0),a[i][j]);
Add(id(i,j,0),S,0);
sum+=a[i][j];
Add(id(i,j,1),T,a[i][j]);
Add(T,id(i,j,1),0);
}
}
while(bfs()) ans+=dfs(S,inf);
cout<<(sum-ans/2)<<'\n';
return 0;
}
P1646 [国家集训队] happiness
题目描述
有一个 \(n*m\) 的矩阵代表学生,学生选文选理都有一定的喜悦值,如果与相邻的人的选科相同则还会有额外的喜悦值,求所有选科方案中最大的喜悦值。
点击查看代码
有不止一种连边方法,先讲题解中的。先不考虑额外的喜悦值,只看单个人的喜悦值,那与源点相连代表选文,与汇点相连代表选理,因为选文和选理矛盾。接着加上相邻人的组合的喜悦值,两人同文的情况与其中某一人选理的情况矛盾,所以可以新建一个节点代表两人同文。
再说一说我想出来的连边方法:将每一个点都拆成两个点,\(i\) 与 \(S\) 通过一条边权为 \(wen[i]\) 的边相连,\(i+n\) 与 \(T\) 通过一条边权为 \(li[i]\) 的边相连,至于额外的贡献,就再建两个新节点分别代表同文和同理,矛盾的点对之间连一条 \(inf\) 边表示即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e17+7;
int n,m,wen[105][105],li[105][105];
int twen[105][105][2][2],tli[105][105][2][2];
int ans,sum,S,T,head[(int)2e6+100],cur[(int)2e6+100];
int tot=1,dis[(int)2e6+100];
struct edge{int to,nex,w;}e[(int)2e6+100];
inline int ID(int x,int y,int tag)
{return (x-1)*(m+2)+y+1+(n+2)*(m+2)*tag;}
void Add(int u,int v,int w)
{e[++tot]={v,head[u],w};head[u]=tot;}
bool bfs() {
queue<int>q;
for(int i=1;i<=T;i++) dis[i]=0,cur[i]=head[i];
dis[S]=1;q.push(S);
while(q.size()) {
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nex) {
int v=e[i].to;
if(e[i].w>0&&dis[v]==0) {
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[T]>0;
}
int dfs(int u,int flow) {
if(u==T||flow==0) return flow;
int f=0;
for(int i=cur[u];i&&flow;i=e[i].nex) {
int v=e[i].to;cur[u]=i;
if(dis[v]==dis[u]+1&&e[i].w>0) {
int op=dfs(v,min(e[i].w,flow));
if(op==0) dis[v]=0;
f+=op;flow-=op;
e[i].w-=op;e[i^1].w+=op;
}
}
return f;
}
signed main() {
cin>>n>>m;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
cin>>wen[i][j],ans=ans+wen[i][j];
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
cin>>li[i][j],ans=ans+li[i][j];
for(int i=1;i< n;i++)for(int j=1;j<=m;j++)
cin>>twen[i][j][1][0],ans=ans+twen[i][j][1][0];
for(int i=1;i< n;i++)for(int j=1;j<=m;j++)
cin>>tli[i][j][1][0],ans=ans+tli[i][j][1][0];
for(int i=1;i<=n;i++)for(int j=1;j< m;j++)
cin>>twen[i][j][0][1],ans=ans+twen[i][j][0][1];
for(int i=1;i<=n;i++)for(int j=1;j< m;j++)
cin>>tli[i][j][0][1],ans=ans+tli[i][j][0][1];
S=(n+2)*(m+2)*6+1;T=S+1;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
Add(S,ID(i,j,0),wen[i][j]);
Add(ID(i,j,0),S,0);
Add(ID(i,j,0),T,li[i][j]);
Add(T,ID(i,j,0),0);
}
for(int i=1;i<n;i++)for(int j=1;j<=m;j++) {
Add(S,ID(i,j,1),twen[i][j][1][0]);
Add(ID(i,j,1),S,0);
Add(ID(i,j,1),ID(i,j,0),inf);
Add(ID(i,j,0),ID(i,j,1),0);
Add(ID(i,j,1),ID(i+1,j,0),inf);
Add(ID(i+1,j,0),ID(i,j,1),0);
Add(ID(i,j,2),T,tli[i][j][1][0]);
Add(T,ID(i,j,2),0);
Add(ID(i,j,0),ID(i,j,2),inf);
Add(ID(i,j,2),ID(i,j,0),0);
Add(ID(i+1,j,0),ID(i,j,2),inf);
Add(ID(i,j,2),ID(i+1,j,0),0);
}
for(int i=1;i<=n;i++)for(int j=1;j<m;j++) {
Add(S,ID(i,j,3),twen[i][j][0][1]);
Add(ID(i,j,3),S,0);
Add(ID(i,j,3),ID(i,j,0),inf);
Add(ID(i,j,0),ID(i,j,3),0);
Add(ID(i,j,3),ID(i,j+1,0),inf);
Add(ID(i,j+1,0),ID(i,j,3),0);
Add(ID(i,j,4),T,tli[i][j][0][1]);
Add(T,ID(i,j,4),0);
Add(ID(i,j,0),ID(i,j,4),inf);
Add(ID(i,j,4),ID(i,j,0),0);
Add(ID(i,j+1,0),ID(i,j,4),inf);
Add(ID(i,j,4),ID(i,j+1,0),0);
}
while(bfs()) ans-=dfs(S,inf);
cout<<ans<<'\n';
return 0;
}
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e17+7;
int n,m,wen[105][105],li[105][105];
int twen[105][105][2][2],tli[105][105][2][2];
int ans,sum,S,T,head[(int)2e6+100],cur[(int)2e6+100];
int tot=1,dis[(int)2e6+100];
struct edge{int to,nex,w;}e[(int)2e6+100];
inline int ID(int x,int y,int tag)
{return (x-1)*(m+2)+y+1+(n+2)*(m+2)*tag;}
void Add(int u,int v,int w)
{e[++tot]={v,head[u],w};head[u]=tot;
e[++tot]={u,head[v],0};head[v]=tot;}
bool bfs() {
queue<int>q;
for(int i=1;i<=T;i++) dis[i]=0,cur[i]=head[i];
dis[S]=1;q.push(S);
while(q.size()) {
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nex) {
int v=e[i].to;
if(e[i].w>0&&dis[v]==0) {
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[T]>0;
}
int dfs(int u,int flow) {
if(u==T||flow==0) return flow;
int f=0;
for(int i=cur[u];i&&flow;i=e[i].nex) {
int v=e[i].to;cur[u]=i;
if(dis[v]==dis[u]+1&&e[i].w>0) {
int op=dfs(v,min(e[i].w,flow));
if(op==0) dis[v]=0;
f+=op;flow-=op;
e[i].w-=op;e[i^1].w+=op;
}
}
return f;
}
signed main() {
cin>>n>>m;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
cin>>wen[i][j],ans=ans+wen[i][j];
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
cin>>li[i][j],ans=ans+li[i][j];
for(int i=1;i< n;i++)for(int j=1;j<=m;j++)
cin>>twen[i][j][1][0],ans=ans+twen[i][j][1][0];
for(int i=1;i< n;i++)for(int j=1;j<=m;j++)
cin>>tli[i][j][1][0],ans=ans+tli[i][j][1][0];
for(int i=1;i<=n;i++)for(int j=1;j< m;j++)
cin>>twen[i][j][0][1],ans=ans+twen[i][j][0][1];
for(int i=1;i<=n;i++)for(int j=1;j< m;j++)
cin>>tli[i][j][0][1],ans=ans+tli[i][j][0][1];
S=(n+2)*(m+2)*6+1;T=S+1;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
Add(S,ID(i,j,0),wen[i][j]);
Add(ID(i,j,1),T,li[i][j]);
Add(ID(i,j,0),ID(i,j,1),inf);
Add(ID(i,j,0),ID(i,j,3),inf);
Add(ID(i,j,0),ID(i,j,5),inf);
Add(ID(i,j,0),ID(i-1,j,5),inf);
Add(ID(i,j,0),ID(i,j-1,3),inf);
Add(ID(i,j,2),ID(i,j,1),inf);
Add(ID(i,j,4),ID(i,j,1),inf);
Add(ID(i,j-1,2),ID(i,j,1),inf);
Add(ID(i-1,j,4),ID(i,j,1),inf);
//同文连同理
Add(ID(i,j,2),ID(i,j,3),inf);
Add(ID(i,j,2),ID(i,j,5),inf);
Add(ID(i,j,2),ID(i-1,j,5),inf);
Add(ID(i,j,2),ID(i,j-1,3),inf);
Add(ID(i,j,4),ID(i,j,3),inf);
Add(ID(i,j,4),ID(i,j,5),inf);
Add(ID(i,j,4),ID(i-1,j,5),inf);
Add(ID(i,j,4),ID(i,j-1,3),inf);
Add(ID(i-1,j,4),ID(i,j,3),inf);
Add(ID(i-1,j,4),ID(i,j,5),inf);
Add(ID(i-1,j,4),ID(i-1,j,5),inf);
Add(ID(i-1,j,4),ID(i,j-1,3),inf);
Add(ID(i,j-1,2),ID(i,j,3),inf);
Add(ID(i,j-1,2),ID(i,j,5),inf);
Add(ID(i,j-1,2),ID(i-1,j,5),inf);
Add(ID(i,j-1,2),ID(i,j-1,3),inf);
}
for(int i=1;i<n;i++)for(int j=1;j<=m;j++) {
Add(S,ID(i,j,4),twen[i][j][1][0]);
Add(ID(i,j,5),T,tli[i][j][1][0]);
}
for(int i=1;i<=n;i++)for(int j=1;j<m;j++) {
Add(S,ID(i,j,2),twen[i][j][0][1]);
Add(ID(i,j,3),T,tli[i][j][0][1]);
}
while(bfs()) ans-=dfs(S,inf);
cout<<ans<<'\n';
return 0;
}
P2046 [NOI2010] 海拔
题目描述
看原题(不儿哥们,链接都在这了)
点击查看代码
首先大胆猜结论:一定存在一种最优方案使得每个点的海拔都为 $0/1$证明:
如果存在海拔为负的点,那么我们让它们都变成 \(0\) 的话,这些点到其它点的花费会减小的同时,这些点内部的花费也变成了零,所以每个点的海拔一定非负。同理,每个点的海拔也不会超过 \(1\)。
再通过反证法证明海拔在取到 \(0/1\) 时最小,假设海拔不都是 \(0/1\) 那对于任意一个海拔不是 \(0/1\) 的联通块 \(i\),它一定不与右下角联通。
-
如果它与左上角联通,我们找到它周围的海拔最低的联通块 \(j\)(设它的海拔为 \(h\)),则与上文同理,有 \(0 \le h_i \le h_j\) ,而它与周围块的花费为一次函数关系,所以最小值一定在端点处取得
-
如果它与左上角不连通,我们让 \(h_i\) 变成 \(h_j\) 一定更优
证毕。
但正常建图的话无法通过 \(2.5e5\) 的数据,我们需要用到对偶图来优化。
对于平面图(即可以画在平面上,边的交点只出现在顶点处的图),它的对偶图使将原图中的每一的面(包括有界面和无界面)视为一个点,对于每一条原图上的边 \(e\),
-
如果 \(e\) 将两个面 $ f_1^{*}$ 和 \(f_2^{*}\) 隔开了,就在对偶图上在 \(f_1^{*}\) 和 \(f_2^{*}\) 之间建一条 \(e.w\) 的边
-
如果 \(e\) 的两边为同一个面,就连一个自环
对偶图有以下性质:
-
对偶图上的一个环对应原图上的一个割(对于一个图,一种删边方式,使得原图不连通,就是原图的一个割)
-
原图的最大流最小割就是对偶图的最短路
所以我们考虑对原图建出对偶图。
如何建对偶图呢
先给源点和汇点连一条虚边,将一个无界面分为两个面,让其中一个面代表源点,另一个面代表汇点,根据定义连边即可。
在对偶图中不能建双向边,每条边可以由原图上的每条边 顺/逆时针旋转得到,至于是顺时针还是逆时针,与源点、汇点代表的面有关。
这样就将时间复杂度变成了 \(O(n logm)\)
#include<bits/stdc++.h>
#define mk make_pair
#define int long long
using namespace std;
const int N=5e6+100,inf=1e16+7;
int n,S,T,tot=1,head[N],ans,dis[N];
bool vis[N];
struct edge{int to,nex;long long w;}e[N<<1];
inline void Add(int u,int v,long long w)
{e[++tot]={v,head[u],w};head[u]=tot;}
inline int ID(int x,int y)
{return (x-1)*(n+2)+y;}
void Dij() {
priority_queue<pair<int,int>,vector<pair<int,int> > ,greater<pair<int,int> > >q;
for(int i=1;i<=T;i++) dis[i]=inf;
dis[S]=0;q.push(mk(dis[S],S));
while(q.size()) {
int u=q.top().second;q.pop();
if(vis[u])continue;vis[u]=1;
for(int i=head[u];i;i=e[i].nex) {
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w) {
dis[v]=dis[u]+e[i].w;
q.push(mk(dis[v],v));
}
}
}
printf("%lld\n",dis[T]);
return ;
}
signed main() {
scanf("%lld",&n);S=(n+3)*(n+3)+1;T=S+1;
for(int i=1;i<=n+1;i++)for(int j=1;j<=n;j++) {
int op;scanf("%lld",&op);
if(i==1) {
Add(ID(i,j),T,op);
// Add(T,ID(i,j),op);
continue;
}
if(i==n+1) {
// Add(ID(i-1,j),S,op);
Add(S,ID(i-1,j),op);
continue;
}
Add(ID(i,j),ID(i-1,j),op);
// Add(ID(i-1,j),ID(i,j),op);
}
for(int i=1;i<=n;i++)for(int j=1;j<=n+1;j++) {
int op;scanf("%lld",&op);
if(j==1) {
Add(S,ID(i,j),op);
// Add(ID(i,j),S,op);
continue;
}
if(j==n+1) {
Add(ID(i,j-1),T,op);
// Add(T,ID(i,j-1),op);
continue;
}
// Add(ID(i,j),ID(i,j-1),op);
Add(ID(i,j-1),ID(i,j),op);
}
for(int i=1;i<=n+1;i++)for(int j=2;j<=n+1;j++) {
int op;scanf("%lld",&op);
if(i==1) {
// Add(ID(i,j-1),T,op);
Add(T,ID(i,j-1),op);
continue;
}
if(i==n+1) {
Add(ID(i-1,j-1),S,op);
// Add(S,ID(i-1,j-1),op);
continue;
}
// Add(ID(i,j-1),ID(i-1,j-1),op);
Add(ID(i-1,j-1),ID(i,j-1),op);
}
for(int i=2;i<=n+1;i++)for(int j=1;j<=n+1;j++) {
int op;scanf("%lld",&op);
if(j==1) {
// Add(S,ID(i-1,j),op);
Add(ID(i-1,j),S,op);
continue;
}
if(j==n+1) {
// Add(ID(i-1,j-1),T,op);
Add(T,ID(i-1,j-1),op);
continue;
}
Add(ID(i-1,j),ID(i-1,j-1),op);
// Add(ID(i-1,j-1),ID(i-1,j),op);
}
Dij();
return 0;
}
P3227 [HNOI2013] 切糕
题目描述
有 \(n*m\) 个纵轴,每个纵轴上选择一个点,相邻纵轴上选择的位置之差不能大与给定的参数 \(D\) ,最小化代价之和
点击查看代码
有两个限制,一个是限制单个纵轴上只能选一个,一个是限制相邻的纵轴之间选数的限制。对于第一个限制,每个点的选择与其它点矛盾,可以将每一条纵轴连在一起,第 \(j\) 个向第 \(j+1\) 连一条 \(a_j\) 的边,被割去就代表被选上了,每一条链只能割一条边,这符合题目中的每一个纵轴只能选一个的要求
对于第二个限制,它就等价于要求每一条链上的第 \(j\) 个与和它相邻的链上的第 \(j-D-1\)、第 \(j-D+2\)...还有第 \(j+D+1\)....不能同时选择,那就要保证在第 \(j\) 个和第 \(j+D+1\) 同时被割去时,源汇点仍然联通
分开考虑即可
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9,N=4e6+100;
const int X[]={1,-1,0,0},Y[]={0,0,1,-1};
int P,Q,R,D,S,T,ans,dis[N],cur[N],head[N],tot=1;
int a[45][45][45];
inline int ID(int x,int y,int z)
{return (z-1)*P*Q+(x-1)*Q+y;}
struct edge{int to,nex,w;}e[N<<1];
inline void Add(int u,int v,int w)
{e[++tot]={v,head[u],w};head[u]=tot;
e[++tot]={u,head[v],0};head[v]=tot;}
inline bool bfs() {
queue<int>q;
for(int i=1;i<=T;i++) dis[i]=0,cur[i]=head[i];
dis[S]=1;q.push(S);
while(q.size()) {
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nex) {
int v=e[i].to;
if(e[i].w>0&&dis[v]==0) {
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[T]>0;
}
inline int dfs(int u,int flow) {
if(u==T||flow==0) return flow;
int f=0;
for(int i=cur[u];i&&flow;i=e[i].nex) {
int v=e[i].to;cur[u]=i;
if(dis[v]==dis[u]+1&&e[i].w>0) {
int op=dfs(v,min(flow,e[i].w));
if(op==0) dis[v]=0;
flow-=op;f+=op;
e[i].w-=op;e[i^1].w+=op;
}
}
return f;
}
signed main() {
scanf("%d%d%d%d",&P,&Q,&R,&D);
S=P*Q*(R+1)*2+50;T=S+1;
for(int z=1;z<=R;z++) for(int x=1;x<=P;x++)
for(int y=1;y<=Q;y++) scanf("%d",&a[x][y][z]);
for(int x=1;x<=P;x++) for(int y=1;y<=Q;y++) {
Add(S,ID(x,y,1),inf);
for(int z=1;z<=R;z++)
Add(ID(x,y,z),ID(x,y,z+1),a[x][y][z]);
Add(ID(x,y,R+1),T,inf);
}
for(int x=1;x<=P;x++) for(int y=1;y<=Q;y++) {
for(int z=1;z<=R;z++) {
for(int op=0;op<4;op++) {
int tx=x+X[op],ty=y+Y[op];
if(tx<1||tx>P||ty<1||ty>Q) continue;
Add(ID(x,y,z),ID(tx,ty,max(z-D,1)),inf);
}
}
}
while(bfs()) ans+=dfs(S,inf);
printf("%d\n",ans);
return 0;
}
P6054 [RC-02] 开门大吉
题目描述
自己去看luogu
点击查看代码
等我口胡
#include<bits/stdc++.h>
using namespace std;
const double inf=1e9,eps=1e-7;
int Tim,n,m,p,y,c[105],S,T,dis[(int)2e6+100];
double f[85][85][85],P[85],Cost[85][85],ans;
int tot=1,head[(int)2e6+100],cur[(int)2e6+100];
struct edge{int to,nex;double w;}e[(int)4e6+100];
inline int ID(int x,int y)
{return (x-1)*(m+2)+y;}
void Add(int u,int v,double w)
{e[++tot]={v,head[u],w};head[u]=tot;
e[++tot]={u,head[v],0};head[v]=tot;}
bool bfs() {
for(int i=1;i<=T;i++) dis[i]=0,cur[i]=head[i];
queue<int>q;dis[S]=1;q.push(S);
while(q.size()) {
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nex) {
int v=e[i].to;
if(dis[v]==0&&(e[i].w>eps)) {
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[T]>0;
}
double dfs(int u,double flow) {
if(u==T||fabs(flow)<eps) return flow;
double f=0;
for(int i=cur[u];i&&(flow>eps);i=e[i].nex) {
int v=e[i].to;cur[u]=i;
if(dis[v]==dis[u]+1&&e[i].w>eps) {
double op=dfs(v,min(flow,e[i].w));
if(fabs(op)<eps) dis[v]=0;
f+=op;flow-=op;
e[i].w-=op;e[i^1].w+=op;
}
}
return f;
}
signed main(){
cin>>Tim;
while(Tim--) {
cin>>n>>m>>p>>y;
for(int i=1;i<=p;i++) cin>>c[i];
for(int i=2;i<=p;i++) c[i]+=c[i-1];
for(int j=1;j<=m;j++) for(int i=1;i<=n;i++)
for(int k=1;k<=p;k++) cin>>f[i][j][k];
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++){
P[1]=f[i][j][1];
for(int k=2;k<=p;k++) P[k]=P[k-1]*f[i][j][k];
double op=0;
for(int k=2;k<=p;k++)
op=op+P[k-1]*(1-f[i][j][k])*c[k-1];
op=op+P[p]*c[p];
Cost[i][j]=op;
}
}
S=(m+2)*(n+2)+1;T=S+1;
for(int i=1;i<=n;i++) {
Add(S,ID(i,1),inf);
for(int j=1;j<=m;j++)Add(ID(i,j),ID(i,j+1),Cost[i][j]);
Add(ID(i,m+1),T,inf);
}
for(int q=1;q<=y;q++) {
int i,j,k;cin>>i>>j>>k;
for(int idj=1;idj<=m;idj++) {
int idi=idj+k;
if(idi>=1&&idi<=m) {
Add(ID(j,idj),ID(i,idi),inf);
continue;
}
if(idi<1) {continue;}
if(idi>m) Add(ID(j,idj),ID(j,idj+1),inf);
}
}
ans=0;
while(bfs()&&ans<=inf) ans+=dfs(S,inf);
if(ans>inf) printf("-1\n");
else printf("%.7lf\n",ans);
tot=1;for(int i=1;i<=T;i++) head[i]=cur[i]=0;
}
return 0;
}
丢一道对偶图练习题 P4001 [ICPC-Beijing 2006] 狼抓兔子
犹小之影
posted on 2025-08-01 21:37 Pearblossom 阅读(30) 评论(3) 收藏 举报