美食节
动态加边的思想很棒qwq,我们SPFA的时候有的点显然用不到的(费用大不会增广)所以先不加这些边。
然后就是。。洛谷这题。。卡常数,有2个点不开O2我卡不进去啊qwqwq
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e7+5;
const int INF=1e9+7;
struct edge{
int u,to,next,w,c;
}e[MAXN<<1];
int n,m,s,t,tot=0;
int head[MAXN],cnt=1;
inline void add(int u,int v,int w,int cost){
e[++cnt]=(edge){u,v,head[u],w,cost},head[u]=cnt;
e[++cnt]=(edge){v,u,head[v],0,-cost},head[v]=cnt;
}
queue<int>qq;
int dis[81005],pre[81005];
bool vis[81005];
bool SPFA(int x){
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
for(int i=s;i<=t;i++)dis[i]=INF;
qq.push(x);
dis[x]=0;vis[x]=1;
while(qq.size()){
int u=qq.front();qq.pop();vis[u]=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].c;
if(e[i].w){
if(dis[u]+w<dis[v]){
dis[v]=dis[u]+w;pre[v]=i;
if(!vis[v]){
qq.push(v);vis[v]=1;
}
}
}
}
}
if(dis[t]==INF)return 0;
return 1;
}
int cai[200][200];
int chushi[200];
int num=0;
int MC(){
int ans=0,flow=0;
while(1){
num++;
if(!SPFA(s))break;
int tem=INF;
for(int i=pre[t];i;i=pre[e[i].u]){
tem=min(tem,e[i].w);
}
int t1=0;
for(int i=pre[t];i;i=pre[e[i].u]){
ans+=e[i].c*tem;
e[i].w-=tem;e[i^1].w+=tem;
}
t1=e[pre[t]].u;
t1-=n;
int t2=t1,t3;
t2%=m;//第几个厨师
if(!t2){
t2=m;
t3=t1/m-1;//前面的
}
else t3=t1/m;
flow+=tem;
for(int i=1;i<=n;i++){
add(i,n+(t3-1)*m+t2,1,cai[i][t2]*(tot-t3+1));
}
add(n+(t3-1)*m+t2,t,1,0);
}
return ans;
}
int tmp[MAXN];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int tem;
scanf("%d",&tmp[i]);
tot+=tmp[i];
} //n+1->n+tot*m;厨师
// 一个厨师是tot个点。每列编号递增
s=0,t=tot*m+n+1;
for(int i=1;i<=n;i++)add(s,i,tmp[i],0);//1-n菜
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&cai[i][j]);//第i个菜 第j厨师
}
}
for(int i=n+(tot-1)*m+1;i<=n+tot*m;i++){
add(i,t,1,0);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
add(i,n+(tot-1)*m+j,1,cai[i][j]);
}
}
printf("%d\n",MC());
return 0;
}
分治最小割、最小割树。用于统计两两点之间最小割。
任意选两个点,然后dfs找S集T集、统计S集,T集中点对最小割,在S集T集中分治。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
const int INF=1e9+7;
inline int mymin(int x,int y){return x>y?y:x;}
int a[MAXN],n,m,s,t,ansflow[170][170];
bool markd[MAXN];
struct edge{
int to,next,w;
}e[MAXN<<1];
int head[MAXN],cur[MAXN],cnt;
inline void add(int u,int v,int w){
e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
}
queue<int>q;
int dep[MAXN];
bool bfs(int x){
memset(dep,0,sizeof(dep));
q.push(x);dep[x]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(w&&!dep[v]){
dep[v]=dep[u]+1;q.push(v);
}
}
}
if(!dep[t])return 0;
return 1;
}
int dfs(int u,int flow){
if(u==t||flow==0)return flow;
for(int &i=cur[u];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(w&&dep[v]==dep[u]+1){
int tem=dfs(v,mymin(w,flow));
if(tem){
e[i].w-=tem;
e[i^1].w+=tem;
return tem;
}
}
}
return 0;
}
void mem(){
memset(e,0,sizeof(e));
memset(a,0,sizeof(a));
memset(head,0,sizeof(head));
memset(ansflow,0x3f,sizeof(ansflow));
cnt=1;
}
void re(){
for(int i=2;i<=cnt;i+=2){
e[i].w=e[i^1].w=((e[i].w+e[i^1].w)>>1);
}
}
void color(int u){
markd[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(w&&!markd[v])color(v);
}
}
int tmp[MAXN];
void solve(int l,int r){
if(l==r)return;
re();
s=a[l],t=a[r];
int ans=0;
while(bfs(s)){
for(int i=1;i<=n;i++)cur[i]=head[i];
while(int d=dfs(s,INF))ans+=d;
}
memset(markd,0,sizeof(markd));
color(s);
for(int i=1;i<=n;i++)
if(markd[i])
for(int j=1;j<=n;j++)
if(!markd[j]){
ansflow[i][j]=ansflow[j][i]=mymin(ans,ansflow[i][j]);
}
int L=l,R=r;
for(int i=l;i<=r;i++){
if(markd[a[i]])tmp[L++]=a[i];
else tmp[R--]=a[i];
}
for(int i=l;i<=r;i++){
a[i]=tmp[i];
}
solve(l,L-1);
solve(R+1,r);
}
void work(){
mem();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
}
for(int i=1;i<=n;i++)a[i]=i;
int q;
solve(1,n);
scanf("%d",&q);
for(int i=1;i<=q;i++){
int x;
scanf("%d",&x);
int output=0;
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++){
if(ansflow[i][j]<=x)output++;
}
printf("%d\n",output);
}
printf("\n");
}
int main(){
int num;
scanf("%d",&num);
while(num--)work();
return 0;
}
不同的最小割
这道题做法和ZJOI最小割是差不多的,如果数组开不够,用个set或map应该可以统计答案。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
const int INF=1e9+7;
inline int mymin(int x,int y){return x>y?y:x;}
struct edge{
int to,next,w;
}e[MAXN<<1];
int n,m,s,t;
int head[MAXN],cur[MAXN],cnt=1;
int ansflow[900][900];
inline void add(int u,int v,int w){
e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
}
queue<int>q;
int dep[MAXN];
bool bfs(int x){
memset(dep,0,sizeof(dep));
q.push(x);dep[x]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(w&&!dep[v]){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
if(!dep[t])return 0;
return 1;
}
int dfs(int u,int flow){
if(u==t||flow==0)return flow;
for(int &i=cur[u];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(w&&dep[v]==dep[u]+1){
int tem=dfs(v,mymin(w,flow));
if(tem){
e[i].w-=tem;
e[i^1].w+=tem;
return tem;
}
}
}
return 0;
}
void re(){
for(int i=2;i<=cnt;i+=2){
e[i].w=e[i^1].w=(e[i].w+e[i^1].w)>>1;
}
}
bool markd[MAXN];
void color(int u){
markd[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(w&&!markd[v])color(v);
}
}
int tmp[1000],a[1000];
void solve(int l,int r){
if(l==r)return;
re();
int ans=0;
s=a[l],t=a[r];//原图里随便选两个点源汇
while(bfs(s)){
for(int i=1;i<=n;i++)cur[i]=head[i];
while(int d=dfs(s,INF))ans+=d;
}
memset(markd,0,sizeof(markd));
color(s);
for(int i=1;i<=n;i++)if(markd[i])
for(int j=1;j<=n;j++)if(!markd[j]){
ansflow[i][j]=ansflow[j][i]=mymin(ansflow[i][j],ans);
}
int L=l,R=r;
for(int i=l;i<=r;i++){
if(markd[a[i]])tmp[L++]=a[i];//这个地方肯定要真实值。
else tmp[R--]=a[i];
}
for(int i=l;i<=r;i++)a[i]=tmp[i];
solve(l,L-1);
solve(R+1,r);
}
bool query[80000005];
int main(){
memset(query,0,sizeof(query));
memset(ansflow,0x3f,sizeof(ansflow));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
for(int i=1;i<=n;i++)a[i]=i;
solve(1,n);
int output=0;
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++){
if(!query[ansflow[i][j]]){
output++;
query[ansflow[i][j]]=1;
}
}
printf("%d\n",output);
return 0;
}
交换棋子
显然要拆点。
我们发现从起始交换到目标状态的路径上,中间交换次数为2,两端点为1。从i交换到j的总交换次数为(dis(i,j)-1)*2。
每个起始和目标状态点都可以少交换一次。
所以起始和目标状态点的限制次数+1.
容量可以设置为限制次数/2。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
const int base=1e5;
const int INF=1e9+7;
inline int mymin(int x,int y){return x>y?y:x;}
int cnt=1,head[MAXN],n,m,s,t;
struct edge{
int u,to,next,w,c;
}e[MAXN<<1];
inline void add(int u,int v,int w,int cost){
e[++cnt]=(edge){u,v,head[u],w,cost},head[u]=cnt;
e[++cnt]=(edge){v,u,head[v],0,-cost},head[v]=cnt;
}
bool vis[MAXN];
int dis[MAXN],pre[MAXN];
queue<int>q;
bool SPFA(int x){
memset(pre,0,sizeof(pre));
for(int i=s;i<=t;i++)dis[i]=INF;
q.push(x);dis[x]=0;vis[x]=1;
while(q.size()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].c;
if(e[i].w&&dis[u]+w<dis[v]){
dis[v]=dis[u]+w;pre[v]=i;
if(!vis[v]){
vis[v]=1;q.push(v);
}
}
}
}
if(dis[t]==INF)return 0;
return 1;
}
int flow=0;
int MC(){
int ans=0;
while(SPFA(s)){
int tem=INF;
for(int i=pre[t];i;i=pre[e[i].u]){
tem=mymin(e[i].w,tem);
}
for(int i=pre[t];i;i=pre[e[i].u]){
e[i].w-=tem;
e[i^1].w+=tem;
ans+=e[i].c*tem;
}
flow+=tem;
}
return ans;
}
char tem[MAXN];
int chushi[25][25],mubiao[25][25],xianzhi[25][25];
void init(){
for(int i=1;i<=n;i++){
scanf("%s",tem+1);
for(int j=1;j<=m;j++)
chushi[i][j]=tem[j]-'0';
}
for(int i=1;i<=n;i++){
scanf("%s",tem+1);
for(int j=1;j<=m;j++)
mubiao[i][j]=tem[j]-'0';
}
for(int i=1;i<=n;i++){
scanf("%s",tem+1);
for(int j=1;j<=m;j++)
xianzhi[i][j]=tem[j]-'0';
}
}
int judge(int x,int y){//9*9有没有1
if(chushi[x][y]==1&&mubiao[x][y]==1)return 2;
if(chushi[x][y]==1||mubiao[x][y]==1)return 1;
return 0;
}
int main(){
int qizi=0;
scanf("%d%d",&n,&m);
init();
s=0,t=n*m+base+1;
for(int i=1;i<=n*m;i++){//i入 i+base出
int lie=i%m,hang;
if(!lie) lie=m,hang=i/m;
else hang=(i/m)+1;
int p=judge(hang,lie);
if(p==1)add(i,i+base,(xianzhi[hang][lie]+1)/2,0);
else if(p==2)add(i,i+base,(xianzhi[hang][lie]+2)/2,0);
else add(i,i+base,xianzhi[hang][lie]/2,0);
if(hang!=1)add(i+base,i-m,INF,1);//上
if(hang!=n)add(i+base,i+m,INF,1);//下
if(lie!=1)add(i+base,i-1,INF,1);//左
if(lie!=m)add(i+base,i+1,INF,1);//右
if(hang!=1&&lie!=1)add(i+base,i-m-1,INF,1);//上左
if(hang!=1&&lie!=m)add(i+base,i-m+1,INF,1);//上右
if(hang!=n&&lie!=1)add(i+base,i+m-1,INF,1);//下左
if(hang!=n&&lie!=m)add(i+base,i+m+1,INF,1);//下右
if(chushi[hang][lie]==1)add(s,i,1,0),qizi++;
if(mubiao[hang][lie]==1)add(i+base,t,1,0);
}
int ans=MC();
if(qizi==flow)printf("%d\n",ans);
else printf("-1\n");
return 0;
}