最近写了下《线性规划与网络流24题》,发下代码和题解,事实上就是将交给 cy c y 的题解复制一下
T1 飞行员配对方案问题
solution
裸的匈牙利
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
const int maxn=205;
int n,m,e,p=0;
int cp[maxn];
bool used[maxn];
struct node {int next,v;}a[maxn*maxn];
int head[maxn];
int ans=0;
void init();
template <typename _TP> inline void read(_TP &x){
x=0;char c11=getchar();bool booo=0;
while(c11<'0'||c11>'9'){if(c11=='-')booo=1;c11=getchar();}
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
if(booo)x=-x;return ;
}
bool find(int);
inline void add(int,int);
void work(){
for(int i=1;i<=m;i++){
cl(used);
if(find(i))ans++;
}
printf("%d\n",ans);
for(int i=m+1;i<=m+n;i++)
if(cp[i])printf("%d %d\n",cp[i],i-m);
return ;
}
int main(){
init();
work();
return 0;
}
bool find(int u){
for(int i=head[u];i;i=a[i].next){
int v=a[i].v;
if(!used[v]){
used[v]=1;
if(!cp[v]||find(cp[v])){
cp[v]=u;
return 1;
}
}
}
return 0;
}
void init(){
read(m);read(n);
cl(cp);cl(used);cl(used);
int A,B;
while(1){
read(A);read(B);
if(A==B && B==-1)return ;
B+=m;
add(A,B);
}
return ;
}
inline void add(int lala,int haha){
a[++p].v=haha;
a[p].next=head[lala];
head[lala]=p;
return ;
}
T2 太空飞行计划
solution
考虑补集,要不不选实验,要不就选仪器,由于实验是收益,仪器是花费,所以在最终的答案上加上所有收益,就可以将收益变负,可以跑最小割
code
/*
这题的读入鬼畜,输出方案鬼畜,换行鬼畜
*/
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define inf 0x3f3f3f3f
#define rg register
#define ADD(x,y,z) add(x,y,z);add(y,x,0)
char c11;
template <typename _Tp> inline void read(_Tp &x){
x=0;c11=getchar();
while(c11<'0'||c11>'9')c11=getchar();
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
}
const int N=55,M=30000;
struct node{int v,w,nxt;}a[M];
int head[M],cur[M],dis[M];
int s,t,n,m;
int p=0;
int tot=0;
bool bo[M];
inline void add(int u,int v,int w){
a[p].v=v;
a[p].w=w;
a[p].nxt=head[u];
head[u]=p++;
}
void init();
bool bfs(){
queue <int> q;
cl1(dis);
dis[s]=0;
q.push(s);
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==-1&&a[i].w>0)
{dis[a[i].v]=dis[x]+1;q.push(a[i].v);if(a[i].v==t)break;}
}
return dis[t]!=-1;
}
int dfs(int x,int low){
if(x==t||low==0)return low;
int temp,res=0;
for(rg int i=cur[x];~i;i=a[i].nxt)
if(dis[a[i].v]==dis[x]+1)
if((temp=dfs(a[i].v,min(low-res,a[i].w)))>0){
a[i].w-=temp;
a[i^1].w+=temp;
res+=temp;
if(x>m&&x<=m+n&&low>a[i].w&&a[i].v==t)bo[x-m]=1;
if(res==low)return low;
}
if(!res)dis[x]=-1;
return res;
}
void work(){
int ans=0;
while(bfs()){
for(rg int i=1;i<=t;++i)cur[i]=head[i];
ans+=dfs(s,inf);
}
//bfs();
for(rg int i=1;i<=m;++i)if(~dis[i])printf("%d ",i);
putchar('\n');
for(rg int i=m+1;i<=m+n;++i)if(~dis[i])printf("%d ",i-m);
printf("\n%d\n",tot-ans);
return ;
}
int main(){
freopen("in","r",stdin);
init();
work();
return 0;
}
void init(){
char c[1000];
read(m);read(n);
cl1(head);
s=m+n+1,t=m+n+2;
int A;
for(rg int i=1;i<=m;++i){
scanf("%d",&A);tot+=A;
add(s,i,A),add(i,s,0);
while("**** you"){
read(A);
add(i,m+A,inf),add(m+A,i,0);
if(c11=='\n'||c11=='\r')break;
}
}
for(rg int i=1;i<=n;++i){
read(A);
add(m+i,t,A),add(t,m+i,0);
}
return ;
}
T3 最小路径覆盖
solution
对于原图中的每一个点i,我们把它拆分成Ai和Bi,对于原图中每一条有向边(ui,vi),我们将ui左图中对应的点Aui连向vi右图中对应点Bvi,最后
不相交最小路径覆盖=原图中总节点数−新构二分图中最大匹配证明
因为对与一张有向图,最坏情况下就是每个点作为一条路径,但谁都知道我们不会傻到一条路径解决一个点,所以这时我们从最坏情况考虑,逐步减少总路径数
我们发现,只有当两个点之间有联通关系且它们仍未被联通时才可以将其合并为一条路径
自然地想到并查集,但是由于并查集之间的元素无方向性,所以只有带权并查集加上一些玄学操作才能解决问题
但我们懒(ben)呀,所以我们巧妙地使用二分图解决这个问题:
因为每个点既可以作为出发点,也可以作为到达点,而它在作为到达点时只能作为一条路径的到达点,作为出发点时也是只能作为一条路径的出发点,但实际上很难将这个点的出发状态与到达状态区分,所以我们使用二分图区分状态
对于每一条匹配成功的边都表示着可以减少一条最坏情况下(每个点都是一条路)的路
所以最终我们得到的结论是
最小不相交路径覆盖=节点数−新构二分图最大匹配
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define rg register
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;
while(c11<'0'||c11>'9')c11=getchar();
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
}
struct node{int v,nxt;}a[3000000];
int head[155],p=0;
int n,m;
bool bo[155],used[155];
int fr[155],to[155];
int ans=0;
inline void add(int,int);
void init();
int find(int x){
for(int i=head[x];i;i=a[i].nxt)
if(!used[a[i].v]){
used[a[i].v]=1;
if(!fr[a[i].v]||find(fr[a[i].v])){
fr[a[i].v]=x;
return 1;
}
}
return 0;
}
void work(){
cl(fr);
for(rg int i=1;i<=n;++i){
cl(used);
if(find(i))++ans;
}
}
void print(){
cl(to);
for(rg int i=1;i<=n;++i)to[fr[i]]=i;
cl(bo);
for(rg int i=1;i<=n;++i)if(!bo[i]){
int x=i;
while(fr[x])x=fr[x];
while("boom"){
bo[x]=1;
printf("%d ",x);
if(to[x])x=to[x];
else break;
}
putchar('\n');
}
printf("%d\n",n-ans);
}
int main(){
init();
work();
print();
return 0;
}
void init(){
read(n);read(m);
int A,B;
for(rg int i=0;i<m;++i){
read(A);read(B);
add(A,B);
}
return ;
}
inline void add(int u,int v){
a[++p].v=v;
a[p].nxt=head[u];
head[u]=p;
}
T4 魔术球问题
solution
很显然可以将符合要求的俩球连起来,显然有n个柱子就像n条路径将图覆盖,直接视作最小路径覆盖
然而这题要求已知路径覆盖,求最大点数
大家都是二分答案做网络流的
但我的方法是,直接将边全部反向(这样的话每次加一个球所加的边全部从一个点出发而不是汇聚成一点,方便匈牙利),上匈牙利枚举,因为匈牙利是找n回可行方案,所以可以一个球一个球地加上去,每次找一次,最终相当于直接对答案做匈牙利
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define rg register
bool bo[20000],used[20000];
int fr[20000],to[20000];
struct node{int v,nxt;}a[100000];
int head[20000],p=0;
int tot=0;
inline void add(int,int);
int find(int x){
for(rg int i=head[x];i;i=a[i].nxt)
if(!used[a[i].v]){
used[a[i].v]=1;
if(!fr[a[i].v]||find(fr[a[i].v])){
fr[a[i].v]=x;
return 1;
}
}
return 0;
}
int work(int x){
cl(used);
for(rg int i=1;i<x;++i){
int tem=x+i,sq=sqrt(tem);
if(sq*sq==tem||(sq-1)*(sq-1)==tem||(sq+1)*(sq+1)==tem)
add(x,i);
}
if(find(x))++tot;
return tot;
}
void solve(int x){
printf("%d\n",x);
p=0;cl(head);cl(fr);cl(to);
for(rg int i=1;i<x;++i)
for(rg int j=i+1;j<=x;++j){
int tem=i+j,sq=sqrt(tem);
if(sq*sq==tem||(sq-1)*(sq-1)==tem||(sq+1)*(sq+1)==tem)
add(i,j);
}
int et=0;
for(rg int i=1;i<=x;++i){
cl(used);
if(find(i))++et;
}
for(rg int i=1;i<=x;++i)to[fr[i]]=i;
cl(bo);
int now;
for(rg int i=1;i<=x;++i)if(!bo[i]){
now=i;
while(1){
printf("%d ",now);bo[now]=1;
now=to[now];
if(!now)break;
}
putchar('\n');
}
return ;
}
int main(){
int Ans;
std::cin>>Ans;
for(rg int n=1;;++n)
if(n-work(n)>Ans)
{solve(n-1);return 0;}
}
inline void add(int u,int v){
a[++p].v=v;
a[p].nxt=head[u];
head[u]=p;
}
T5 圆桌问题
solution
一道很明显的二分图
从源点连向每个公司,流量为公司人数 //限制该公司人数
从每个公司连向每个桌子,流量为1 //限制同一桌子同一公司仅一人
从每个桌子连向汇点,流量为桌子大小 //限制桌子人数
然后跑最大流,看是否等于人数总和
code
#include<bits/stdc++.h>
using namespace std;
#define ADD(x,y,z) add(x,y,z);add(y,x,0)
#define rg register
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define inf 0x3f3f3f3f
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&(c11<'0'||c11>'9'))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=80030;
struct node{int v,w,nxt;}a[1213021];
int head[N];
int n,m,s,t;
int dis[N],xila=0;
int ans=0,p=0;
inline void add(int,int,int);
void init();
bool bfs(){
queue <int> q;
q.push(s);
cl1(dis);
dis[s]=0;
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==-1&&a[i].w>0)
{dis[a[i].v]=dis[x]+1;q.push(a[i].v);if(a[i].v==t)break;}
}
return dis[t]!=-1;
}
int dfs(int x,int low){
if(x==t||low==0)return low;
int temp,res=0;
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==dis[x]+1)
if((temp=dfs(a[i].v,min(low,a[i].w)))>0){
a[i].w-=temp;
a[i^1].w+=temp;
res+=temp;
if(res==low)return low;
}
if(!res)dis[x]=-1;
return res;
}
void work(){
while(bfs())
ans+=dfs(s,inf);
}
int str[10000];
void print(){
printf("%d\n",ans==xila);
if(xila!=ans)return ;
int top=0;
for(rg int x=1;x<=m;++x,putchar('\n'),top=0){
for(rg int i=head[x];~i;i=a[i].nxt)
if(a[i].v>=m+1&&a[i].v<=m+n)
if(a[i].w==0)
str[top++]=a[i].v-m;
sort(str,str+top);
for(rg int i=0;i<top;++i)printf("%d ",str[i]);
}
return ;
}
int main(){
init();
work();
print();
return 0;
}
void init(){
read(m);read(n);cl1(head);
s=n+m+1,t=n+m+2;
int A;
for(rg int i=1;i<=m;++i){
read(A);xila+=A;
ADD(s,i,A);
}
for(rg int i=m+1;i<=m+n;++i){
read(A);
ADD(i,t,A);
}
for(rg int i=1;i<=m;++i)
for(rg int j=m+1;j<=m+n;++j)
{ADD(i,j,1);}
return ;
}
inline void add(int u,int v,int w){
a[p].v=v;
a[p].w=w;
a[p].nxt=head[u];
head[u]=p++;
}
T6 最长递增子序列问题
solution
话说这题思维难度还是有的
这题之所以往网络流想是因为出现了路径条数
第一问
直接dp
第二问
由于每一个点仅能取一次,所以自然想到拆点连流量为1的边限制使用次数
再利用dp的数组将所有f[i]+=f[j]的i和j连起来,最后在伪二分图中求方案数就相当于最大流
第三问
将对1号和n号的限制取消,或直接变流量为inf
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define min(x,y) (((x)<(y))?(x):(y))
#define max(x,y) (((x)>(y))?(x):(y))
#define rg register
#define inf 0x3f3f3f3f
#define ADD(x,y,z) add(x,y,z);add(y,x,0)
const int N=1050;
struct node{int v,nxt,w;}a[100000];
int head[N],p=0;
int dis[N],f[N],S[N];
int n,s,t;
int K=0;
template <typename _Tp> inline void read(_Tp&x){char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;
}
inline void add(int,int,int);
void init();
void work1();
void work2();
void work3();
bool bfs(){
queue <int> q;
cl1(dis);
dis[s]=0;
q.push(s);
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==-1&&a[i].w>0)
{dis[a[i].v]=dis[x]+1;q.push(a[i].v);if(a[i].v==t)break;}
}
return dis[t]!=-1;
}
int dfs(int x,int low){
if(x==t||low==0)return low;
int res=0,temp;
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==dis[x]+1)
if((temp=dfs(a[i].v,min(low-res,a[i].w)))>0){
a[i].w-=temp;
a[i^1].w+=temp;
res+=temp;
if(res==low)return low;
}
if(!res)dis[x]=-1;
return res;
}
int main(){
// freopen("in","r",stdin);
// freopen("1.out","w",stdout);
init();
work1();
work2();
work3();
return 0;
}
void work3(){
cl1(head);p=0;
for(rg int i=1;i<=n;++i)
if(f[i]==K){ADD(s,i,1);}
for(rg int i=1;i<=n;++i){ADD(i,i+n,1);}
for(rg int i=1;i<n;++i)
for(rg int j=i+1;j<=n;++j)
if(f[i]==f[j]+1&&S[i]<=S[j])
{ADD(i+n,j,1);}
for(rg int i=1;i<=n;++i)
if(f[i]==1){ADD(i+n,t,1);}
for(rg int i=head[s];~i;i=a[i].nxt)
if(a[i].v==1)a[i].w=inf;
for(rg int i=head[1];~i;i=a[i].nxt)
if(a[i].v==n+1)a[i].w=inf;
for(rg int i=head[n+n];~i;i=a[i].nxt)
if(a[i].v==t)a[i].w=inf;
for(rg int i=head[n];~i;i=a[i].nxt)
if(a[i].v==n+n)a[i].w=inf;
int temp=0;
while(bfs())
temp+=dfs(s,inf);
printf("%d\n",temp);
return ;
}
void work2(){
cl1(head);p=0;
for(rg int i=1;i<=n;++i)
if(f[i]==K){ADD(s,i,1);}
for(rg int i=1;i<=n;++i){ADD(i,i+n,1);}
for(rg int i=1;i<n;++i)
for(rg int j=i+1;j<=n;++j)
if(f[i]==f[j]+1&&S[i]<=S[j])
{ADD(i+n,j,1);}
for(rg int i=1;i<=n;++i)
if(f[i]==1){ADD(i+n,t,1);}
int temp=0;
while(bfs())
temp+=dfs(s,inf);
printf("%d\n",temp);
return ;
}
void work1(){
for(rg int i=n;i;--i){
int ma=0;
for(rg int j=i+1;j<=n;++j)
if(f[j]>ma&&S[i]<=S[j])ma=f[j];
f[i]=ma+1;
K=max(K,f[i]);
}
printf("%d\n",K);
}
void init(){
read(n);
s=n<<1|1;t=s+1;
for(rg int i=1;i<=n;++i)read(S[i]);
return ;
}
inline void add(int u,int v,int w){
a[p].v=v;
a[p].w=w;
a[p].nxt=head[u];
head[u]=p++;
}
T7 试题库问题
solution
这题就是圆桌问题换了个题面,一个二分图建图
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define rg register
#define inf 0x3f3f3f3f
#define min(x,y) ((x)<(y)?(x):(y))
template <typename _Tp> inline void read(_Tp&x){char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){c11=getchar();booo=1;}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=1050,K=25,L=N+K+100;
vector <int> vec[K];
int head[L],cur[L],dis[L],p=0;
struct node {int v,nxt,w;}a[1000000];
int n,k,s,t;
int ans=0,m=0;
inline void add(int,int,int);
inline void ADD(int x,int y,int z){add(x,y,z);add(y,x,0);return ;}
void init();
bool bfs(){
queue <int> q;
q.push(s);
cl1(dis);
dis[s]=0;
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==-1&&a[i].w>0)
{dis[a[i].v]=dis[x]+1;q.push(a[i].v);if(a[i].v==t)break;}
}
return dis[t]!=-1;
}
int dfs(int x,int low){
if(x==t||low==0)return low;
int res=0,temp;
for(rg int i=cur[x];~i;i=a[i].nxt){
cur[x]=i;
if(dis[a[i].v]==dis[x]+1)
if((temp=dfs(a[i].v,min(low-res,a[i].w)))>0){
a[i].w-=temp;
a[i^1].w+=temp;
res+=temp;
if(res==low)return low;
}
}
cur[x]=head[x];
if(!res)dis[x]=-1;
return res;
}
void work(){
while(bfs()){
for(rg int i=1;i<=t;++i)cur[i]=head[i];
ans+=dfs(s,inf);
}
}
void print(){
if(ans!=m){puts("No Solution!");return ;}
for(rg int i=head[s];~i;i=a[i].nxt)if(a[i].w==0&&a[i].v>=1&&a[i].v<=n){
int x=a[i].v;
for(rg int j=head[x];~j;j=a[j].nxt)if(a[j].v>=n+1&&a[j].v<=n+k)
if(a[j].w==0){vec[a[j].v-n].push_back(x);break;}
}
for(rg int i=1;i<=k;++i){
printf("%d:",i);
for(rg int j=vec[i].size()-1;j>-1;--j)
printf(" %d",vec[i][j]);
printf("\n");
}
return ;
}
int main(){
init();
work();
print();
return 0;
}
void init(){
read(k);read(n);
cl1(head);p=0;
s=n+k+1,t=n+k+2;
int A;
for(rg int i=1;i<=k;++i){
read(A);m+=A;
ADD(n+i,t,A);
}
for(rg int i=1;i<=n;++i)ADD(s,i,1);
int poi;
for(rg int i=1;i<=n;++i){
read(poi);
for(rg int j=1;j<=poi;++j){
read(A);
ADD(i,n+A,1);
}
}
return ;
}
inline void add(int u,int v,int w){
a[p].v=v;
a[p].w=w;
a[p].nxt=head[u];
head[u]=p++;
}
T9 方格取数问题
solution
一开始想的就是将每个点的上向下连边,左到右连边,求最小割,后来发现这种独立集问题好像只能在二分图中运作,所以将图奇偶染色,拆成二分图,答案为总权值减去最小割
code
#include<bits/stdc++.h>
using namespace std;
#define qi(x,y) ((x&1)^(y&1))
#define get(x,y) ((x-1)*n+y)
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define rg register
#define inf 0x3f3f3f3f
template <typename _Tp> inline void read(_Tp&x){char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;
}
const int MD=100050,ME=100050;
struct node{int v,w,nxt;}a[ME];
int head[MD],cur[MD],p=0;
int n,m,s,t;
int dis[MD];
int tot=0,ans=0;
inline void add(int,int,int);
inline void ADD(int,int,int);
void init();
bool bfs(){
queue <int> q;
q.push(s);
cl1(dis);
dis[s]=0;
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==-1&&a[i].w>0)
{dis[a[i].v]=dis[x]+1;q.push(a[i].v);if(a[i].v==t)break;}
}
return dis[t]!=-1;
}
int dfs(int x,int low){
if(x==t||low==0)return low;
int res=0,temp;
for(rg int i=cur[x];~i;i=a[i].nxt){
cur[x]=i;
if(dis[a[i].v]==dis[x]+1)
if((temp=dfs(a[i].v,min(low-res,a[i].w)))>0){
a[i].w-=temp;
a[i^1].w+=temp;
res+=temp;
if(res==low)return low;
}
}
if(!res)dis[x]=-1;
return res;
}
void work(){
while(bfs()){
for(rg int i=1;i<=t;++i)cur[i]=head[i];
ans+=dfs(s,inf);
}
printf("%d\n",tot-ans);
return ;
}
int main(){
init();
work();
return 0;
}
void init(){
read(m);read(n);
cl1(head);
s=n*m+1,t=s+1;
int A;
for(rg int i=1;i<=m;++i)
for(rg int j=1;j<=n;++j){
read(A);tot+=A;
if(qi(i,j)){
ADD(s,get(i,j),A);
if(i<m)ADD(get(i,j),get(i+1,j),inf);
if(j<n)ADD(get(i,j),get(i,j+1),inf);
if(i>1)ADD(get(i,j),get(i-1,j),inf);
if(j>1)ADD(get(i,j),get(i,j-1),inf);
}
else ADD(get(i,j),t,A);
}
return ;
}
inline void add(int u,int v,int w){
a[p].v=v;
a[p].w=w;
a[p].nxt=head[u];
head[u]=p++;
}
inline void ADD(int x,int y,int z){add(x,y,z);add(y,x,0);return ;}
T10 餐巾计划问题
solution
一看到最小,又没有什么方法转换成补集,所以应该是费用流,
只要模拟建图即可:
首先将每天拆成需求点与舍弃点
“每天可以用p分买纸巾” –> 从s到每天的需求点连费用为p,流量为inf的边
“每天可以洗纸巾” –> 从当前天的舍弃点连向(洗衣时间)天后的需求点,费用为(洗衣费),流量inf
“最后纸巾不用洗干净” –> 从每一天的舍弃点连向后一天的舍弃点
最后为了检验是否每天满足要求,从每一天的需求点连向t,流量为当天需求量(保证没有多余纸巾浪费),费用0;
最后检验连向t的边是否都是满流,是则为满足(由于对于每条路径都有从s直接到需求点再到t的路径,所以肯定是满流)
所以由于最大流已经确定,可以放心地求同样满足每天需求情况下的最小费用
最后答案为最小费用最大流
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define rg register
#define inf 0x3f3f3f3f
#define min(x,y) (((x)<(y))?(x):(y))
template <typename _Tp> inline void read(_Tp&x){
char c11=getchar();x=0;
while(!isdigit(c11))c11=getchar();
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}
}
const int N=15050,M=1505050;
int mf,mt,kt,kf,p,s,t,n,r[N];ll ans=0;
int head[N],_=0;
struct node{int v,nxt,w,cap;}a[M];
ll dis[N],flow[N];int fa[N];
bool in[N];
inline void add(int,int,int,int);
inline void ADD(int,int,int,int);
void init();
void pre();
int spfa(){
deque <int> q;
clm(dis);cl(in);dis[t]=inf;
dis[s]=0;flow[s]=inf;
in[s]=1;
q.push_back(s);
while(!q.empty()){
int x=q.front();
in[x]=0;
q.pop_front();
for(rg int i=head[x];~i;i=a[i].nxt)if(a[i].cap>0)
if(dis[a[i].v]>dis[x]+a[i].w){
//if(a[i].w==0&&~dis[a[i].v])continue;
dis[a[i].v]=dis[x]+a[i].w;
fa[a[i].v]=i;
flow[a[i].v]=min(flow[x],a[i].cap);
if(!in[a[i].v]){
in[a[i].v]=1;
if(q.empty())q.push_back(a[i].v);
else if(dis[q.front()]<dis[x])q.push_back(a[i].v);
else q.push_front(a[i].v);
}
}
}
return dis[t]!=inf;
}
void work(){
while(spfa()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
ans+=flow[t]*dis[t];
}
printf("%lld\n",ans);
return ;
}
int main(){
init();
pre();
work();
return 0;
}
void pre(){
for(rg int i=1;i<=n;++i){
ADD(s,i,r[i],0);
ADD(i+n,t,r[i],0);
ADD(s,i+n,inf,p);
if(i+kt<=n)ADD(i,i+kt+n,inf,kf);
if(i+mt<=n)ADD(i,i+mt+n,inf,mf);
if(i<n)ADD(i,i+1,inf,0);
}
return ;
}
void init(){
read(n);cl1(head);
s=n*2+1;t=s+1;
for(rg int i=1;i<=n;++i)read(r[i]);
read(p);read(kt);read(kf);read(mt);read(mf);
return ;
}
inline void ADD(int aa,int bb,int cc,int dd){add(aa,bb,cc,dd);add(bb,aa,0,-dd);return ;}
inline void add(int u,int v,int cap,int w){
a[_].v=v;
a[_].w=w;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T11 航空路线问题
solution
这题由于每个点仅能到一次,所以使用我仅知的限制点访问次数的方法——拆点连边(流量1)
由于只有两次机会访问,所以要求最后的最大流为2,采用将1号点和n号点的自身限制(流量)改为2
(也是为了适应1号点和n号点能访问多次的特点)由于要查询哪些点被访问过,这时自身限制的边就有了第二个作用(计数),只要将费用设为1,最后最大费用所得值即为计数器即答案
最后求1号点到n号点的最小费用最大流
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define oo 0x3f3f3f3f
#define rg register
#define inf 0x3f3f3f3f
#define min(x,y) ((x)<(y)?(x):(y))
#define out(x) std::cout<<city[x]<<endl
template <typename _Tp> inline void read(_Tp&x){char c11=getchar();x=0;
while(!isdigit(c11))c11=getchar();while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}return ;
}
const int N=500,M=100000;
struct node{int v,nxt,w,cap;}a[M];
int n,m,s,t;
int _=0,mcost=0,mflow=0;
int flow[N],dis[N],head[N],fa[N];
bool in[N];
string city[N];
map <string,int> mp;
inline void add(int,int,int,int);
inline void ADD(int,int,int,int);
void init();
bool spfa(){
queue <int> q;
for(rg int i=1;i<=t;++i)dis[i]=-oo;
cl(in);cl(fa);
dis[s]=0;in[s]=1;flow[s]=inf;
q.push(s);
while(!q.empty()){
int x=q.front();
in[x]=0;
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]<dis[x]+a[i].w&&a[i].cap>0){
fa[a[i].v]=i;
dis[a[i].v]=dis[x]+a[i].w;
flow[a[i].v]=min(flow[x],a[i].cap);
if(!in[a[i].v]){
in[a[i].v]=1;
q.push(a[i].v);
}
}
}
return dis[t]!=-oo;
}
bool work(){
mflow=0,mcost=0;
while(spfa()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
mflow+=flow[t];
mcost+=flow[t]*dis[t];
}
if(mflow==2){printf("%d\n",mcost-2);return 1;}
if(mflow==1&&mcost==2){puts("2");return 1;}
puts("No Solution!");
return 0;
}
int str[N],top=0;
void print(){
if(mflow==1&&mcost==2){out(1);out(n);out(1);return ;}
int x=s+n;
out(1);
while(x!=t){
for(rg int i=head[x];~i;i=a[i].nxt)
if(a[i].cap==0&&a[i].v>=1&&a[i].v<=n){
x=a[i].v+n;
out(a[i].v);
a[i].cap=1;
break;
}
}
x=s+n;
while(x!=t){
for(rg int i=head[x];~i;i=a[i].nxt)
if(a[i].cap==0&&a[i].v>=1&&a[i].v<=n){
x=a[i].v+n;
str[++top]=a[i].v;
a[i].cap=1;
break;
}
}
for(rg int i=top-1;i;--i)out(str[i]);
out(1);
return ;
}
int main(){
init();
if(work())
print();
return 0;
}
void init(){
read(n);read(m);
cl1(head);
s=1;t=n+n;
for(rg int i=1;i<=n;++i){
std::cin>>city[i];mp[city[i]]=i;
if(i==1||i==n)ADD(i,i+n,2,1);
else ADD(i,i+n,1,1);
}
string A,B;
for(rg int i=0;i<m;++i){
std::cin>>A>>B;
int p1=mp[A],p2=mp[B];
if(p1>p2)swap(p1,p2);
ADD(p1+n,p2,1,0);
}
return ;
}
inline void ADD(int aa,int bb,int cc,int dd){add(aa,bb,cc,dd);add(bb,aa,0,-dd);return ;}
inline void add(int u,int v,int cap,int w){
a[_].v=v;
a[_].cap=cap;
a[_].w=w;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T12 软件补丁问题
solution
话说一看就是最短路裸题
但本着联系网络流的目的
冥思苦想了好久没想出网络流做法,网上也没搜到,dalao也不知道
所以最后还是臣服于最短路%%%
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define rg register
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;bool booo=0;
while((c11<'0'||c11>'9')&&c11!='-')c11=getchar();if(c11=='-'){c11=getchar();booo=1;}
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;
}
const int N=1000000,M=1000000;
int wa[M],ac[M],era[M],ad[M],t[M];
struct node{int v,nxt,w;}a[M];
int dis[N];
int head[N],p=0;
bool bo[N];
int n,m;
inline void add(int u,int v,int w){
a[++p].v=v;
a[p].w=w;
a[p].nxt=head[u];
head[u]=p;
return ;
}
void init();
void spfa(int S,int T){
queue <int> q;
cl1(dis);cl(bo);
dis[S]=0;
q.push(S);
while(!q.empty()){
int x=q.front();
q.pop();
if(!bo[x]){
for(rg int i=1;i<=m;++i)
if(((x&wa[i])==wa[i])&&((x&ac[i])==0))
add(x,x^(x&era[i])|ad[i],t[i]);
bo[x]=1;
}
for(rg int i=head[x];i;i=a[i].nxt)
if(dis[a[i].v]==-1||dis[a[i].v]>dis[x]+a[i].w){
dis[a[i].v]=dis[x]+a[i].w;
q.push(a[i].v);
}
}
return ;
}
int main(){
init();
spfa((1<<n)-1,0);
printf("%d\n",dis[0]==-1?0:dis[0]);
return 0;
}
void init(){
read(n);read(m);
cl(wa);cl(ac);cl(era);cl(ad);
char s1[100],s2[100];
for(rg int i=1;i<=m;++i){
read(t[i]);
scanf("%s%s",s1,s2);
int len1=strlen(s1),len2=strlen(s2);
for(rg int j=0;j<len1;++j)
if(s1[j]=='+')wa[i]|=(1<<j);
else if(s1[j]=='-')ac[i]|=(1<<j);
for(rg int j=0;j<len2;++j)
if(s2[j]=='+')ad[i]|=(1<<j);
else if(s2[j]=='-')era[i]|=(1<<j);
}
return ;
}
T13 星际转移问题
solution
这题很像雅加达的摩天楼,然而……确实很像
事实上就是将雅加达的摩天楼的分块换为分层图,最短路换为最大流,好像就做完了哈
顺便发一下各种小细节的时间
二分 枚举
每次重新建图 40ms 30ms?
残余网络加边 161ms 0ms
可以看出残余网络还是很强的,能怼掉二分的时间优化
code
#include<bits/stdc++.h>
using namespace std;
#define get(x,y) ((t)*(x)+(y))
#define cl(x) memset(x,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define rg register
#define min(x,y) ((x)<(y)?(x):(y))
#define inf 0x3f3f3f3f
template <typename _Tp> inline void read(_Tp&x){char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
//#define read(x) scanf("%d",&x)
const int N=100010,M=50;
int w[M],sum[M],k,m,n;
int lit[M][N+3];
struct node{int v,nxt,w;}a[1000050];
int head[N],_=0,dis[N],cur[N],dad[N];
int S,T,s,t;
inline int father(int x){return dad[x]?dad[x]=father(dad[x]):x;}
inline void add(int,int,int);
inline void ADD(int,int,int);
void init();
bool bfs(){
queue <int> q;
cl1(dis);
dis[S]=0;
q.push(S);
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==-1&&a[i].w>0)
{dis[a[i].v]=dis[x]+1;q.push(a[i].v);if(a[i].v==T)break;}
}
return dis[T]!=-1;
}
int dfs(int x,int low){
if(x==T||low==0)return low;
int res=0,temp;
for(rg int i=head[x];~i;i=a[i].nxt){
cur[x]=i;
if(dis[a[i].v]==dis[x]+1&&a[i].w>0)
if((temp=dfs(a[i].v,min(low-res,a[i].w)))>0){
a[i].w-=temp;
a[i^1].w+=temp;
res+=temp;
if(res==low)return low;
}
}
if(!res)dis[x]=-1;
return res;
}
int solve(int day){
int ans=0;
ADD(S,get(day,s),inf);
ADD(get(day,t),T,inf);
for(rg int i=1;i<=m;++i)
ADD(get(day-1,lit[i][(day-1)%sum[i]]),get(day,lit[i][day%sum[i]]),w[i]);
for(rg int i=1;i<=n;++i)ADD(get(day-1,i),get(day,i),inf);
while(bfs()){
for(rg int i=1;i<=get(day,t);++i)cur[i]=head[i];cur[S]=head[S];cur[T]=head[T];
ans+=dfs(S,inf);
}
return ans;
}
void work(){
int Ans=0,temp=0;
ADD(S,get(0,s),inf);
while("%%%%%% boshi %%%%%%"){
temp+=solve(++Ans);
printf("%d\n",_-1);
if(temp>=k){
printf("%d %d\n",Ans,_-1);
return ;
}
}
return ;
}
bool check(){
for(rg int i=1;i<=m;++i)
for(rg int j=1;j<sum[i];++j)
if(father(lit[i][j-1])!=father(lit[i][j]))
dad[father(lit[i][j-1])]=father(lit[i][j]);
if(father(s)==father(t))return 1;
puts("0");
return 0;
}
int main(){
init();
if(check())
work();
return 0;
}
void init(){
read(n);read(m);read(k);
cl1(head);
s=n+1,t=n+2;S=N-2,T=N-1;
for(rg int i=1;i<=m;++i){
read(w[i]);
read(sum[i]);
for(rg int j=0;j<sum[i];++j){
read(lit[i][j]);
if(lit[i][j]==0)lit[i][j]=s;
if(lit[i][j]==-1)lit[i][j]=t;
}
}
return ;
}
inline void ADD(int u,int v,int w){/*printf("%d -> %d %d\n",u,v,w);*/add(u,v,w);add(v,u,0);return ;}
inline void add(int u,int v,int w){
a[_].w=w;
a[_].v=v;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T14 孤岛营救问题
solution
我也不知道这题为什么是网络流
直接上 bfs or ID or 分层图最短路
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define inf (0x3f3f3f3f)
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rg register
template <typename _Tp> inline void read(_Tp&x){char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){c11=getchar();booo=1;}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int mx[]={0,0,1,-1};
const int my[]={1,-1,0,0};
const int N=15,M=15;
int wall[N][M][4],Key[N][M];
bool bo[N][M][(1<<10)+10];
int n,m,p,k;
struct node{int x,y,key,step;};
set <node> c;
void init();
int bfs(){
queue <node> q;
c.clear();cl(bo);
node fir;
fir.x=1,fir.y=1,fir.key=0,fir.step=0;
while(!q.empty())q.pop();
q.push(fir);
while(!q.empty()){
node now=q.front();
int x=now.x,y=now.y,key=now.key,step=now.step;
q.pop();
for(rg int i=0;i<4;++i){
if(wall[x][y][i]==0)continue;
if(((1<<wall[x][y][i])&key)==0&&wall[x][y][i]!=-1)continue;
if(x+mx[i]<1||x+mx[i]>n||y+my[i]<1||y+my[i]>m)continue;
node nxt;
nxt.x=x+mx[i],nxt.y=y+my[i];
nxt.key=(key|Key[x+mx[i]][y+my[i]]);
nxt.step=step+1;
if(nxt.x==n&&nxt.y==m)return nxt.step;
if(!bo[nxt.x][nxt.y][nxt.key]){bo[nxt.x][nxt.y][nxt.key]=1;q.push(nxt);}
}
}
return -1;
}
int main(){
init();
printf("%d\n",bfs());
return 0;
}
void init(){
read(n);read(m);read(p);
read(k);cl1(wall);
int x1,y1,x2,y2,x,y,A,T;
for(rg int i=0;i<k;++i){
read(x1);read(y1);read(x2);read(y2);read(A);
for(rg int j=0;j<4;++j)
if(x1+mx[j]==x2&&y1+my[j]==y2)
{wall[x1][y1][j]=A;wall[x2][y2][j^1]=A;break;}
}
read(T);
for(rg int i=0;i<T;++i){
read(x);read(y);read(A);
Key[x][y]|=(1<<A);
}
return ;
}
T15 汽车加油行驶问题
solution
本以为是广搜,发现方格图太大会T
再想根据加油站作为点,连边spfa
在打完输入后想到一个问题,就是当俩加油站连边时不能仅依照曼哈顿距离判断,有可能中间有加油站拦腰截断强制收过路费最后发现k十分小,分层图spfa就过了
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define inf 0x3f3f3f3f
#define oo 0x7fffffff
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rg register
#define get(x,y,z) ((x-1)*(n)+(y)+(z)*(n*n))
template <typename _Tp> inline void read(_Tp&x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=105,K=12;
int dis[N*N*K],head[N*N*K];
struct node{int v,nxt,w;}a[1000005];
int n,A,B,C,k,s,_=0;
const int mx[]={0,0,1,-1},my[]={1,-1,0,0};
inline void add(int,int,int);
void init();
void spfa(){
deque <int> q;
cl1(dis);
dis[s]=0;
q.push_back(s);
while(!q.empty()){
int x=q.front();
q.pop_front();
for(rg int i=head[x];i;i=a[i].nxt)
if(dis[a[i].v]==-1||dis[a[i].v]>dis[x]+a[i].w){
dis[a[i].v]=dis[x]+a[i].w;
if(q.empty())q.push_back(a[i].v);
else if(dis[q.front()]>dis[a[i].v])q.push_front(a[i].v);
else q.push_back(a[i].v);
}
}
return ;
}
void print(){
int ans=inf;
for(rg int i=0;i<=k;++i)
ans=min(ans,dis[get(n,n,i)]);
printf("%d\n",ans);
return ;
}
int main(){
init();
spfa();
//for(rg int i=1;i<=100;++i)printf("%d %d\n",i,a[i].v);
print();
return 0;
}
void init(){
read(n);read(k);read(A);read(B);read(C);
s=get(1,1,k);
int Z;
for(rg int i=1;i<=n;++i)
for(rg int j=1;j<=n;++j){
read(Z);
if(Z){
for(rg int y=0;y<k;++y)
add(get(i,j,y),get(i,j,k),A);
for(rg int f=0;f<4;++f)if(i+mx[f]<1||i+mx[f]>n||j+my[f]<1||j+my[f]>n);else
add(get(i,j,k),get(i+mx[f],j+my[f],k-1),(mx[f]<0||my[f]<0)?B:0);
}
else{
for(rg int y=1;y<=k;++y)
for(rg int f=0;f<4;++f)if(i+mx[f]<1||i+mx[f]>n||j+my[f]<1||j+my[f]>n);else
add(get(i,j,y),get(i+mx[f],j+my[f],y-1),(mx[f]<0||my[f]<0)?B:0);
//for(rg int f=0;f<4;++f)
add(get(i,j,0),get(i,j,k),A+C);
}
}
return ;
}
inline void add(int u,int v,int w){
a[++_].v=v;
a[_].w=w;
a[_].nxt=head[u];
head[u]=_;
return ;
}
T16 数字梯形问题
solution
明显联想最小路径覆盖
想了想还是网络流
日常拆点自我限制访问次数
三小问的区别就是自身访问限制的大小和路径大小
第一问:正常做
第二问:自身访问限制变为inf
第三问:除了总源点连向第一层的边不变之外,其他所有边的流量限制变为inf
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define rg register
#define min(x,y) ((x)<(y)?(x):(y))
#define inf 0x3f3f3f3f
#define oo 0x3f3f3f3f
#define get(x,y,z) ((x-1)*(m+n-1)+(y)+((z)-1)*(n)*(m+n-1))
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&(c11<'0'||c11>'9'))c11=getchar();if(c11=='-'){c11=getchar();booo=1;}
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=24,M=24,T=1000;
struct node{int v,nxt,w,cap;}a[1000000];
int head[T],dis[T],flow[T],fr[T],_=0;
bool in[T];
int num[N][M];
int s,t,n,m;
inline void ADD(int,int,int,int);
inline void add(int,int,int,int);
void init();
void work1();
void work2();
void work3();
bool spfa(){
queue <int> q;
for(rg int i=1;i<=t;++i)dis[i]=-oo;
cl(in);cl(fr);
dis[s]=0;in[s]=1;flow[s]=inf;
q.push(s);
while(!q.empty()){
int x=q.front();
in[x]=0;
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]<dis[x]+a[i].w&&a[i].cap>0){
fr[a[i].v]=i;
dis[a[i].v]=dis[x]+a[i].w;
flow[a[i].v]=min(flow[x],a[i].cap);
if(!in[a[i].v]){
in[a[i].v]=1;
q.push(a[i].v);
}
}
}
return dis[t]!=-oo;
}
int mcmf(){
int mc=0;
while(spfa()){
int x=t;
while(x!=s){
a[fr[x]].cap-=flow[t];
a[fr[x]^1].cap+=flow[t];
x=a[fr[x]^1].v;
}
mc+=dis[t]*flow[t];
}
return mc;
}
int main(){
init();
work1();
work2();
work3();
return 0;
}
void work1(){
for(rg int j=1;j<=m;++j)ADD(s,get(1,j,1),num[1][j],1);
for(rg int i=1;i<n;++i)
for(rg int j=1;j<=m+i-1;++j){
ADD(get(i,j,2),get(i+1,j,1),num[i+1][j],1);
ADD(get(i,j,2),get(i+1,j+1,1),num[i+1][j+1],1);
ADD(get(i,j,1),get(i,j,2),0,1);
}
for(rg int j=1;j<=m+n-1;++j){ADD(get(n,j,1),get(n,j,2),0,1);ADD(get(n,j,2),t,0,1);}
printf("%d\n",mcmf());
return ;
}
void work2(){
_=0;cl1(head);
for(rg int j=1;j<=m;++j)ADD(s,get(1,j,1),num[1][j],1);
for(rg int i=1;i<n;++i)
for(rg int j=1;j<=m+i-1;++j){
ADD(get(i,j,2),get(i+1,j,1),num[i+1][j],1);
ADD(get(i,j,2),get(i+1,j+1,1),num[i+1][j+1],1);
ADD(get(i,j,1),get(i,j,2),0,inf);
}
for(rg int j=1;j<=m+n-1;++j){ADD(get(n,j,1),get(n,j,2),0,inf);ADD(get(n,j,2),t,0,inf);}
printf("%d\n",mcmf());
return ;
}
void work3(){
_=0;cl1(head);
for(rg int j=1;j<=m;++j)ADD(s,get(1,j,1),num[1][j],1);
for(rg int i=1;i<n;++i)
for(rg int j=1;j<=m+i-1;++j){
ADD(get(i,j,2),get(i+1,j,1),num[i+1][j],inf);
ADD(get(i,j,2),get(i+1,j+1,1),num[i+1][j+1],inf);
ADD(get(i,j,1),get(i,j,2),0,inf);
}
for(rg int j=1;j<=m+n-1;++j){ADD(get(n,j,1),get(n,j,2),0,inf);ADD(get(n,j,2),t,0,inf);}
printf("%d\n",mcmf());
return ;
}
void init(){
cl1(head);
read(m);read(n);
s=T-2,t=T-1;
for(rg int i=1;i<=n;++i)
for(rg int j=1;j<=m+i-1;++j)
read(num[i][j]);
return ;
}
inline void ADD(int u,int v,int w,int cap){add(u,v,w,cap);add(v,u,-w,0);return ;}
inline void add(int u,int v,int w,int cap){
a[_].w=w;
a[_].v=v;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T17 运输问题
solution
裸的网络流
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define rg register
#define min(x,y) ((x)<(y)?(x):(y))
#define inf 0x3f3f3f3f
#define oo 0x3f3f3f3f
#define get(x,y,z) ((x-1)*(m+n-1)+(y)+((z)-1)*(n)*(m+n-1))
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&(c11<'0'||c11>'9'))c11=getchar();if(c11=='-'){c11=getchar();booo=1;}
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=105,M=105,T=105+105;
struct node{int v,nxt,cap,c;}a[100000];
int head[T],dis[T],flow[T],fa[T];
bool in[T];
int n,m,_=0,s,t;
int fir[T],sec[T],thi[N][N];
inline void ADD(int,int,int,int);
inline void add(int,int,int,int);
void init();
void work1();
void work2();
bool bfs1();
bool bfs2();
void pre();
int main(){
init();
work1();
pre();
work2();
return 0;
}
void pre(){
cl1(head);_=0;
for(rg int i=1;i<=m;++i)
ADD(s,i,0,fir[i]);
for(rg int i=1;i<=n;++i)
ADD(i+m,t,0,sec[i]);
for(rg int i=1;i<=m;++i)
for(rg int j=1;j<=n;++j)
ADD(i,j+m,thi[i][j],inf);
return ;
}
bool bfs2(){
cl(fa);cl(in);
for(rg int i=1;i<=t;++i)dis[i]=-oo;
queue <int> q;
q.push(s);
in[s]=1;flow[s]=inf;
dis[s]=0;
while(!q.empty()){
int x=q.front();
in[x]=0;
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(a[i].cap>0&&dis[a[i].v]<dis[x]+a[i].c){
dis[a[i].v]=dis[x]+a[i].c;
fa[a[i].v]=i;
flow[a[i].v]=min(flow[x],a[i].cap);
if(!in[x]){
q.push(a[i].v);
in[a[i].v]=1;
}
}
}
return dis[t]!=-oo;
}
bool bfs1(){
cl(fa);cl(in);clm(dis);
queue <int> q;
q.push(s);
in[s]=1;flow[s]=inf;
dis[s]=0;
while(!q.empty()){
int x=q.front();
in[x]=0;
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(a[i].cap>0&&dis[a[i].v]>dis[x]+a[i].c){
dis[a[i].v]=dis[x]+a[i].c;
fa[a[i].v]=i;
flow[a[i].v]=min(flow[x],a[i].cap);
if(!in[x]){
q.push(a[i].v);
in[a[i].v]=1;
}
}
}
return dis[t]<inf;
}
void work2(){
int mcost=0;
while(bfs2()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
mcost+=flow[t]*dis[t];
}
printf("%d\n",mcost);
}
void work1(){
int mcost=0;
while(bfs1()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
mcost+=flow[t]*dis[t];
}
printf("%d\n",mcost);
}
void init(){
read(m);read(n);
s=m+n+1;t=s+1;
int A;cl1(head);
for(rg int i=1;i<=m;++i){
read(A);fir[i]=A;
ADD(s,i,0,A);
}
for(rg int i=1;i<=n;++i){
read(A);sec[i]=A;
ADD(i+m,t,0,A);
}
for(rg int i=1;i<=m;++i)
for(rg int j=1;j<=n;++j){
read(A);thi[i][j]=A;
ADD(i,j+m,A,inf);
}
return ;
}
inline void ADD(int u,int v,int c,int cap){add(u,v,c,cap);add(v,u,-c,0);return ;}
inline void add(int u,int v,int c,int cap){
a[_].v=v;
a[_].c=c;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T18 分配问题
solution
和T17一模一样
直接复制将m赋为n就A了
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define rg register
#define min(x,y) ((x)<(y)?(x):(y))
#define inf 0x3f3f3f3f
#define oo 0x3f3f3f3f
#define get(x,y,z) ((x-1)*(m+n-1)+(y)+((z)-1)*(n)*(m+n-1))
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&(c11<'0'||c11>'9'))c11=getchar();if(c11=='-'){c11=getchar();booo=1;}
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=105,M=105,T=105+105;
struct node{int v,nxt,cap,c;}a[100000];
int head[T],dis[T],flow[T],fa[T];
bool in[T];
int n,m,_=0,s,t;
int fir[T],sec[T],thi[N][N];
inline void ADD(int,int,int,int);
inline void add(int,int,int,int);
void init();
void work1();
void work2();
bool bfs1();
bool bfs2();
void pre();
int main(){
init();
work1();
pre();
work2();
return 0;
}
void pre(){
cl1(head);_=0;
for(rg int i=1;i<=m;++i)
ADD(s,i,0,fir[i]);
for(rg int i=1;i<=n;++i)
ADD(i+m,t,0,sec[i]);
for(rg int i=1;i<=m;++i)
for(rg int j=1;j<=n;++j)
ADD(i,j+m,thi[i][j],inf);
return ;
}
bool bfs2(){
cl(fa);cl(in);
for(rg int i=1;i<=t;++i)dis[i]=-oo;
queue <int> q;
q.push(s);
in[s]=1;flow[s]=inf;
dis[s]=0;
while(!q.empty()){
int x=q.front();
in[x]=0;
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(a[i].cap>0&&dis[a[i].v]<dis[x]+a[i].c){
dis[a[i].v]=dis[x]+a[i].c;
fa[a[i].v]=i;
flow[a[i].v]=min(flow[x],a[i].cap);
if(!in[x]){
q.push(a[i].v);
in[a[i].v]=1;
}
}
}
return dis[t]!=-oo;
}
bool bfs1(){
cl(fa);cl(in);clm(dis);
queue <int> q;
q.push(s);
in[s]=1;flow[s]=inf;
dis[s]=0;
while(!q.empty()){
int x=q.front();
in[x]=0;
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(a[i].cap>0&&dis[a[i].v]>dis[x]+a[i].c){
dis[a[i].v]=dis[x]+a[i].c;
fa[a[i].v]=i;
flow[a[i].v]=min(flow[x],a[i].cap);
if(!in[x]){
q.push(a[i].v);
in[a[i].v]=1;
}
}
}
return dis[t]<inf;
}
void work2(){
int mcost=0;
while(bfs2()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
mcost+=flow[t]*dis[t];
}
printf("%d\n",mcost);
}
void work1(){
int mcost=0;
while(bfs1()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
mcost+=flow[t]*dis[t];
}
printf("%d\n",mcost);
}
void init(){
read(n);m=n;
s=m+n+1;t=s+1;
int A;cl1(head);
for(rg int i=1;i<=m;++i){
fir[i]=1;
ADD(s,i,0,1);
}
for(rg int i=1;i<=n;++i){
sec[i]=1;
ADD(i+m,t,0,1);
}
for(rg int i=1;i<=m;++i)
for(rg int j=1;j<=n;++j){
read(A);thi[i][j]=A;
ADD(i,j+m,A,inf);
}
return ;
}
inline void ADD(int u,int v,int c,int cap){add(u,v,c,cap);add(v,u,-c,0);return ;}
inline void add(int u,int v,int c,int cap){
a[_].v=v;
a[_].c=c;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T19 负载平衡问题
solution
本题一开始以为是最小可行流问题,再仔细想想,发现直接跑最大流即可
但建图有两种方法
①直接建图 (题解的做法)
连相邻两点,费用1,流量inf
连 s到每个点,费用0,流量为该数
连 每个点到t,费用0,流量为所有数平均值
②差值建图 (自己一开始想到的是这个)
其余都是一样
只是当一个数大于平均值时,连s到该数,费用0,流量为该数与平均值的差值
当一个数小于平均值时,连该数到t,费用0,流量为该数与平均值的差值
这样每个点就只用向s或t连一条边,而不是两边都连了
但好像这个方法比上边那个慢1ms,难道是数据水?
code _Method_1
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define clt(x,y) memset(x,y,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rg register
#define inf 0x7fffffff
#define oo 0x3f3f3f3f
template <typename _Tp> inline void read(_Tp&x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=105;
int n,m,s,t;
int flow[N],dis[N],l[N],head[N],fa[N],_=0;
struct node{int v,nxt,w,cap;}a[1000];
int sum=0,aver;
inline void add(int,int,int,int);
inline void ADD(int,int,int,int);
void init();
void pre();
bool spfa(){
deque <int> q;
while(!q.empty())q.pop_front();
q.push_back(s);
clm(dis);clm(flow);cl1(fa);
dis[s]=0;flow[s]=+oo;
while(!q.empty()){
int x=q.front();
q.pop_front();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]>dis[x]+a[i].w&&a[i].cap>0){
dis[a[i].v]=dis[x]+a[i].w;
flow[a[i].v]=min(flow[x],a[i].cap);
fa[a[i].v]=i;
if(q.empty())q.push_back(a[i].v);
else if(dis[q.front()]>dis[a[i].v])q.push_front(a[i].v);
else q.push_back(a[i].v);
}
}
return dis[t]!=+oo;
}
void work(){
int ans=0;
while(spfa()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
ans+=flow[t]*dis[t];
}
printf("%d\n",ans);
return ;
}
int main(){
init();
pre();
work();
return 0;
}
void pre(){
for(rg int i=1;i<=n;++i){
ADD(s,i,l[i],0);
ADD(i,t,aver,0);
if(i!=1)ADD(i,i-1,inf,1);
else ADD(i,n,inf,1);
if(i!=n)ADD(i,i+1,inf,1);
else ADD(i,1,inf,1);
}
return ;
}
void init(){
read(n);cl1(head);
s=n+1;t=n+2;
for(rg int i=1;i<=n;++i){
read(l[i]);
sum+=l[i];
}
aver=sum/n;
return ;
}
inline void ADD(int u,int v,int cap,int w){add(u,v,cap,w);add(v,u,0,-w);return ;}
inline void add(int u,int v,int cap,int w){
a[_].v=v;
a[_].w=w;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
code_Method_2
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define clt(x,y) memset(x,y,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rg register
#define inf 0x7fffffff
#define oo 0x3f3f3f3f
template <typename _Tp> inline void read(_Tp&x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=105;
int n,m,s,t;
int flow[N],dis[N],l[N],head[N],fa[N],_=0;
struct node{int v,nxt,w,cap;}a[1000];
int sum=0,aver;
inline void add(int,int,int,int);
inline void ADD(int,int,int,int);
void init();
void pre();
bool spfa(){
deque <int> q;
while(!q.empty())q.pop_front();
q.push_back(s);
clm(dis);clm(flow);cl1(fa);
dis[s]=0;flow[s]=+oo;
while(!q.empty()){
int x=q.front();
q.pop_front();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]>dis[x]+a[i].w&&a[i].cap>0){
dis[a[i].v]=dis[x]+a[i].w;
flow[a[i].v]=min(flow[x],a[i].cap);
fa[a[i].v]=i;
if(q.empty())q.push_back(a[i].v);
else if(dis[q.front()]>dis[a[i].v])q.push_front(a[i].v);
else q.push_back(a[i].v);
}
}
return dis[t]!=+oo;
}
void work(){
int ans=0;
while(spfa()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
ans+=flow[t]*dis[t];
}
printf("%d\n",ans);
return ;
}
int main(){
init();
pre();
work();
return 0;
}
void pre(){
for(rg int i=1;i<=n;++i){
if(l[i]>0)ADD(s,i,l[i],0);
else ADD(i,t,-l[i],0);
if(i!=1)ADD(i,i-1,inf,1);
else ADD(i,n,inf,1);
if(i!=n)ADD(i,i+1,inf,1);
else ADD(i,1,inf,1);
}
return ;
}
void init(){
read(n);cl1(head);
s=n+1;t=n+2;
for(rg int i=1;i<=n;++i){
read(l[i]);
sum+=l[i];
}
aver=sum/n;
for(rg int i=1;i<=n;++i)
l[i]-=aver;
return ;
}
inline void ADD(int u,int v,int cap,int w){add(u,v,cap,w);add(v,u,0,-w);return ;}
inline void add(int u,int v,int cap,int w){
a[_].v=v;
a[_].w=w;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T20 深海机器人问题
solution
//这题好像“传纸条”,但是dp好像不行,后来发现“传纸条”的正解原来是网络流
求路径条数和限制,求最大覆盖权值
首先由多个入口出口看出是多源多汇网络流,再根据要求求解最大权值(一条路径上的取值是相加而不是取最小值)
所以ans=多源多汇最小费用最大流
至于限制一条边只能走一次,所以一条权值为V的边可以拆成两条边:
一条费用V,流量1
一条费用0,流量inf
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define clt(x,y) memset(x,y,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rg register
#define inf 0x7fffffff
#define oo 0x3f3f3f3f
#define get(x,y) ((x)*(m+1)+(y))
template <typename _Tp> inline void read(_Tp&x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=10050;
int n,m,s,t;
int flow[N],dis[N],l[N],head[N],fa[N],_=0;
struct node{int v,nxt,w,cap;}a[1000000];
int sum=0,aver;
inline void add(int,int,int,int);
inline void ADD(int,int,int,int);
void init();
bool spfa(){
deque <int> q;
while(!q.empty())q.pop_front();
q.push_back(s);
clm(flow);cl1(fa);
for(rg int i=0;i<=t;++i)dis[i]=-oo;
dis[s]=0;flow[s]=+oo;
while(!q.empty()){
int x=q.front();
q.pop_front();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]<dis[x]+a[i].w&&a[i].cap>0){
dis[a[i].v]=dis[x]+a[i].w;
flow[a[i].v]=min(flow[x],a[i].cap);
fa[a[i].v]=i;
if(q.empty())q.push_back(a[i].v);
else if(dis[q.front()]<dis[a[i].v])q.push_front(a[i].v);
else q.push_back(a[i].v);
}
}
return dis[t]!=-oo;
}
void work(){
int ans=0;
while(spfa()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
ans+=flow[t]*dis[t];
}
printf("%d\n",ans);
return ;
}
int main(){
init();
work();
return 0;
}
void init(){
int S,T;
read(S);read(T);
read(n);read(m);cl1(head);
s=(n+1)*(m+1)+1;t=s+1;
int A,B,K;
for(rg int i=0;i<=n;++i)
for(rg int j=0;j<m;++j){
read(A);
ADD(get(i,j),get(i,j+1),1,A);
ADD(get(i,j),get(i,j+1),inf,0);
}
for(rg int j=0;j<=m;++j)
for(rg int i=0;i<n;++i){
read(A);
ADD(get(i,j),get(i+1,j),1,A);
ADD(get(i,j),get(i+1,j),inf,0);
}
for(rg int i=1;i<=S;++i){
read(K);read(A);read(B);
ADD(s,get(A,B),K,0);
}
for(rg int i=1;i<=T;++i){
read(K);read(A);read(B);
ADD(get(A,B),t,K,0);
}
return ;
}
inline void ADD(int u,int v,int cap,int w){add(u,v,cap,w);add(v,u,0,-w);return ;}
inline void add(int u,int v,int cap,int w){
a[_].v=v;
a[_].w=w;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T21 最长k可重区间集问题
solution
一开始认为是像太空飞行计划一般考虑补集,最后用总收益减去最小割,但如果这样的话,就要考虑在线段树上最小割
算了,还是用点简单的,将所有俩不交开区间连起来,随后求一个有向无环图上k线段最大权值覆盖
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define clt(x,y) memset(x,y,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rg register
#define inf 0x7fffffff
#define oo 0x3f3f3f3f
template <typename _Tp> inline void read(_Tp&x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=1005;
int n,m,s,ss,t,k;
int flow[N],dis[N],l[N],head[N],fa[N],_=0;
struct node{int v,nxt,w,cap;}a[1000000];
int sum=0,aver;
struct segm{int l,r;}seg[N];
inline bool my_comp(const segm aa,const segm bb){if(aa.l==bb.l)return aa.r<bb.r;return aa.l<bb.l;}
inline void add(int,int,int,int);
inline void ADD(int,int,int,int);
void init();
bool spfa(){
queue <int> q;
while(!q.empty())q.pop();
q.push(s);
clm(flow);cl1(fa);
for(rg int i=0;i<=ss;++i)dis[i]=-oo;
dis[s]=0;flow[s]=+oo;
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]<dis[x]+a[i].w&&a[i].cap>0){
dis[a[i].v]=dis[x]+a[i].w;
flow[a[i].v]=min(flow[x],a[i].cap);
fa[a[i].v]=i;
q.push(a[i].v);
}
}
return dis[t]!=-oo;
}
void work(){
int ans=0;
while(spfa()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
ans+=flow[t]*dis[t];
}
printf("%d\n",ans);
return ;
}
int main(){
init();
work();
return 0;
}
void init(){
cl1(head);
read(n);read(k);
s=n+n+1,t=n+n+2;ss=n+n+3;
for(rg int i=1;i<=n;++i){
read(seg[i].l);read(seg[i].r);
if(seg[i].l>seg[i].r)swap(seg[i].l,seg[i].r);
}
sort(seg+1,seg+n+1,my_comp);
ADD(s,ss,k,0);
for(rg int i=1;i<=n;++i){
ADD(ss,i,1,0);
ADD(i,i+n,1,seg[i].r-seg[i].l);
ADD(i+n,t,1,0);
}
for(rg int i=1;i<n;++i)
for(rg int j=i+1;j<=n;++j)
if(seg[i].r<=seg[j].l+1)
ADD(i+n,j,1,0);
return ;
}
inline void ADD(int u,int v,int cap,int w){add(u,v,cap,w);add(v,u,0,-w);return ;}
inline void add(int u,int v,int cap,int w){
a[_].v=v;
a[_].w=w;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T22 最长k可重线段集问题
solution
这题和上一题差不多,所给的y值基本上就是用来算线段的权值的
只是要把上一题中的每一条线段的权值变为平面距离,再特判一下两条与y轴平行的线段不连边即可
但这题全网的数据都有错,调了一中午,才发现数据有误,而且只有loj发现有误,将其删除了
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define clt(x,y) memset(x,y,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rg register
#define inf 0x7fffffff
#define oo 0x3f3f3f3f
template <typename _Tp> inline void read(_Tp&x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=1005;
int n,m,s,ss,t,k;
int flow[N],dis[N],l[N],head[N],fa[N],_=0;
struct node{int v,nxt,w,cap;}a[1000000];
int sum=0,aver;
struct segm{int x1,y1,x2,y2,c;}seg[N];
inline bool my_comp(const segm aa,const segm bb){if(aa.x1==bb.x1)return aa.x2<bb.x2;return aa.x1<bb.x1;}
inline void add(int,int,int,int);
inline void ADD(int,int,int,int);
void init();
bool spfa(){
queue <int> q;
while(!q.empty())q.pop();
q.push(s);
clm(flow);cl1(fa);
for(rg int i=0;i<=ss;++i)dis[i]=-oo;
dis[s]=0;flow[s]=+oo;
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]<dis[x]+a[i].w&&a[i].cap>0){
dis[a[i].v]=dis[x]+a[i].w;
flow[a[i].v]=min(flow[x],a[i].cap);
fa[a[i].v]=i;
q.push(a[i].v);
}
}
return dis[t]!=-oo;
}
void work(){
int ans=0;
while(spfa()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
ans+=flow[t]*dis[t];
}
printf("%d\n",ans);
return ;
}
int main(){
init();
work();
return 0;
}
void init(){
cl1(head);
read(n);read(k);
s=n+n+1,t=n+n+2;ss=n+n+3;
for(rg int i=1;i<=n;++i){
read(seg[i].x1);read(seg[i].y1);read(seg[i].x2);read(seg[i].y2);
if(seg[i].x1>seg[i].x2)swap(seg[i].x1,seg[i].x2),swap(seg[i].y1,seg[i].y2);
seg[i].c=(int)sqrt((seg[i].x1-seg[i].x2)*(seg[i].x1-seg[i].x2)+(seg[i].y1-seg[i].y2)*(seg[i].y1-seg[i].y2));
}
sort(seg+1,seg+n+1,my_comp);
ADD(s,ss,k,0);
for(rg int i=1;i<=n;++i){
ADD(ss,i,1,0);
ADD(i,i+n,1,seg[i].c);
ADD(i+n,t,1,0);
}
for(rg int i=1;i<n;++i)
for(rg int j=i+1;j<=n;++j)
if(seg[i].x2<=seg[j].x1)
if(seg[i].x1!=seg[i].x2||seg[j].x1!=seg[j].x2||seg[i].x1!=seg[j].x1)
ADD(i+n,j,1,0);
return ;
}
inline void ADD(int u,int v,int cap,int w){add(u,v,cap,w);add(v,u,0,-w);return ;}
inline void add(int u,int v,int cap,int w){
a[_].v=v;
a[_].w=w;
a[_].cap=cap;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T23 火星探险问题
solution
这题和深海机器人差不多
只是多了一个输出路径,只要利用一条边的原始流量与当前流量的差值就知道当前边有多少车经过,借此输出路径
code
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define clt(x,y) memset(x,y,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rg register
#define inf 0x7fffffff
#define oo 0x3f3f3f3f
#define get(x,y,z) ((x-1)*(m)+(y)+(z-1)*n*m)
template <typename _Tp> inline void read(_Tp&x){
char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}
const int N=505*505+1,T=505;
int n,m,s,t,k;
int flow[N],dis[N],l[N],head[N],fa[N],fir[N],_=0;
int mp[T][T];
struct node{int v,nxt,w,cap,fir;}a[1000000];
int sum=0,aver;
inline void add(int,int,int,int,int);
inline void ADD(int,int,int,int);
void init();
bool spfa(){
queue <int> q;
while(!q.empty())q.pop();
q.push(s);
clm(flow);cl1(fa);
for(rg int i=0;i<=t;++i)dis[i]=-oo;
dis[s]=0;flow[s]=+oo;
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]<dis[x]+a[i].w&&a[i].cap>0){
dis[a[i].v]=dis[x]+a[i].w;
flow[a[i].v]=min(flow[x],a[i].cap);
fa[a[i].v]=i;
q.push(a[i].v);
}
}
return dis[t]!=-oo;
}
void work(){
int ans=0;
while(spfa()){
int x=t;
while(x!=s){
a[fa[x]].cap-=flow[t];
a[fa[x]^1].cap+=flow[t];
x=a[fa[x]^1].v;
}
ans+=flow[t]*dis[t];
}
//printf("%d\n",ans);
return ;
}
void print(){
for(rg int car=1;car<=k;++car){
int x=1;
while(x!=get(n,m,2)){
for(rg int i=head[x];~i;i=a[i].nxt)
if(a[i].cap<a[i].fir&&a[i].fir!=0){
if(x>n*m)
if(a[i].v+n*m==x+1)printf("%d %d\n",car,1);
else printf("%d %d\n",car,0);
a[i].cap++;
x=a[i].v;
break;
}
}
}
}
int main(){
init();
work();
print();
return 0;
}
void init(){
read(k);read(m);read(n);
cl1(head);s=n*m+n*m+1;t=s+1;
for(rg int i=1;i<=n;++i)
for(rg int j=1;j<=m;++j)
read(mp[i][j]);
ADD(s,get(1,1,1),k,0);
for(rg int i=1;i<=n;++i)
for(rg int j=1;j<=m;++j)if(mp[i][j]!=1){
if(i!=n&&mp[i+1][j]!=1)ADD(get(i,j,2),get(i+1,j,1),inf,0);
if(j!=m&&mp[i][j+1]!=1)ADD(get(i,j,2),get(i,j+1,1),inf,0);
}
for(rg int i=1;i<=n;++i)
for(rg int j=1;j<=m;++j)
if(mp[i][j]!=1){
if(mp[i][j]==2)
ADD(get(i,j,1),get(i,j,2),1,1);
ADD(get(i,j,1),get(i,j,2),inf,0);
}
ADD(get(n,m,2),t,k,0);
return ;
}
inline void ADD(int u,int v,int cap,int w){add(u,v,cap,w,cap);add(v,u,0,-w,0);return ;}
inline void add(int u,int v,int cap,int w,int fir){
a[_].v=v;
a[_].w=w;
a[_].cap=cap;
a[_].fir=fir;
a[_].nxt=head[u];
head[u]=_++;
return ;
}
T24 骑士共存问题
solution
这题和方格取数差不多,只是相邻连边变为日字连边
code
#include<bits/stdc++.h>
using namespace std;
#define qi(x,y) ((x&1)^(y&1))
#define get(x,y) ((x-1)*n+y)
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define rg register
#define inf 0x3f3f3f3f
template <typename _Tp> inline void read(_Tp&x){char c11=getchar();x=0;bool booo=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){booo=1;c11=getchar();}
while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;
}
const int MD=800050,ME=800050;
struct node{int v,w,nxt;}a[ME];
int head[MD],cur[MD],p=0;
int n,m,s,t;
int dis[MD];
int has[505][505];
int tot=0,ans=0;
inline void add(int,int,int);
inline void ADD(int,int,int);
void init();
bool bfs(){
queue <int> q;
q.push(s);
cl1(dis);
dis[s]=0;
while(!q.empty()){
int x=q.front();
q.pop();
for(rg int i=head[x];~i;i=a[i].nxt)
if(dis[a[i].v]==-1&&a[i].w>0)
{dis[a[i].v]=dis[x]+1;q.push(a[i].v);if(a[i].v==t)break;}
}
return dis[t]!=-1;
}
int dfs(int x,int low){
if(x==t||low==0)return low;
int res=0,temp;
for(rg int i=cur[x];~i;i=a[i].nxt){
cur[x]=i;
if(dis[a[i].v]==dis[x]+1)
if((temp=dfs(a[i].v,min(low-res,a[i].w)))>0){
a[i].w-=temp;
a[i^1].w+=temp;
res+=temp;
if(res==low)return low;
}
}
if(!res)dis[x]=-1;
return res;
}
void work(){
while(bfs()){
for(rg int i=1;i<=t;++i)cur[i]=head[i];
ans+=dfs(s,inf);
}
printf("%d\n",tot-ans);
return ;
}
int main(){
init();
work();
return 0;
}
void init(){
read(n);read(m);
cl1(head);
s=n*n+1,t=s+1;
tot=n*n-m;
int A=1;
int B,C;
cl(has);
for(rg int i=1;i<=m;++i){
read(B);read(C);
has[B][C]=1;
}
for(rg int i=1;i<=n;++i)
for(rg int j=1;j<=n;++j)if(!has[i][j]){
if(qi(i,j)){
ADD(s,get(i,j),A);
int mx[]={1,1,-1,-1,2,2,-2,-2};
int my[]={2,-2,-2,2,1,-1,-1,1};
for(rg int o=0;o<8;++o)
if(i+mx[o]>=1&&j+my[o]>=1&&i+mx[o]<=n&&j+my[o]<=n&&!has[i+mx[o]][j+my[o]])
ADD(get(i,j),get(i+mx[o],j+my[o]),inf);
}
else ADD(get(i,j),t,A);
}
return ;
}
inline void add(int u,int v,int w){
a[p].v=v;
a[p].w=w;
a[p].nxt=head[u];
head[u]=p++;
}
inline void ADD(int x,int y,int z){add(x,y,z);add(y,x,0);return ;}
做这些题耗了我好长一段时间,这样下去省选岂不GG
而且网络流还有各种各样的题目等着我们
明儿还有月考/(ㄒoㄒ)/~~
从开学到现在总共上了一个月的课,就要和那些上了六七个月课的直升生一起考,简直爆炸