1/24 飞行员配对方案问题
题解:二分匹配
代码:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
int g[maxn][maxn];
int linker[maxn];
bool used[maxn];
int n,m;
bool dfs(int u){
int v;
for(v=0;v<n;v++){
if(g[u][v]&&!used[v]){
used[v]=true;
if(linker[v]==-1||dfs(linker[v])){
linker[v]=u;
return true;
}
}
}
return false;
}
int hungary(){
int res=0;
memset(linker,-1,sizeof(linker));
int u;
for(u=0;u<m;u++){
memset(used,false,sizeof(used));
if(dfs(u))res++;
}
return res;
}
int main()
{
// freopen("1.txt","r",stdin);
int i;
scanf("%d%d",&m,&n);
int a,b;
while(scanf("%d%d",&a,&b)){
if(a+b==-2)break;
a--,b--;
g[a][b]=1;
}
printf("%d\n",hungary());
for(i=0;i<n;i++){
if(linker[i]!=-1)printf("%d %d\n",linker[i]+1,i+1);
}
return 0;
}
2/24 餐巾计划问题
题解:关键是建边。注意纸巾的流入流出关系。将一天拆成上下午,存在的条件为:
上午流量必须大于给定值,上午可以买入任意多流量,下午流量为上午用的流量,可以回流到洗完的天数,纸巾可以洗好留着不用(或者说可以不洗)。
所以要建种边:
1.早上流向汇点,容量need[i],费用0;
2.源点流向早上,容量inf,费用p。
3.源点流向晚上,容量need[i],费用0(早上用过的纸巾)
4.晚上流向晚上,容量inf,费用0(今夜不洗留到明夜)
5.6.今晚留到m天后的早上,容量inf,花费m or n。
代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=4005;
const int maxm=29005;
const long long inf=0x3f3f3f3f;
struct Edge{
int to,next;
ll cap,flow,cost;
}edge[maxm];
int tol;
int head[maxn];
int pre[maxn];
ll dis[maxn];
bool vis[maxn];
int N;
void init(int n){
N=n;
tol=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,long long cap,long long cost){
edge[tol].to=v;
edge[tol].cost=cost;
edge[tol].cap=cap;
edge[tol].flow=0;
edge[tol].next=head[u];
head[u]=tol++;
edge[tol].to=u;
edge[tol].cost=-cost;
edge[tol].cap=0;
edge[tol].flow=0;
edge[tol].next=head[v];
head[v]=tol++;
}
bool spfa(int s,int t){
queue<int>q;
int i;
for(i=0;i<N;i++){
dis[i]=inf;
vis[i]=false;
pre[i]=-1;
}
dis[s]=0;
vis[s]=true;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
for(i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){
dis[v]=dis[u]+edge[i].cost;
pre[v]=i;
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
}
if(pre[t]==-1)return false;
else return true;
}
int mincostmaxflow(int s,int t,long long &cost){
long long flow=0;
cost=0;
while(spfa(s,t)){
long long Min=inf;
int i;
for(i=pre[t];i!=-1;i=pre[edge[i^1].to]){
if(Min>edge[i].cap-edge[i].flow)Min=edge[i].cap-edge[i].flow;
}
for(i=pre[t];i!=-1;i=pre[edge[i^1].to]){
edge[i].flow+=Min;
edge[i^1].flow-=Min;
cost+=edge[i].cost*Min;
}
flow+=Min;
}
return flow;
}
//////////
long long need[maxn];
int p,m,f,mm,s;
int main()
{
int n;
int i;
scanf("%d",&n);
init(2*n+2);
for(i=1;i<=n;i++){
scanf("%lld",&need[i]);
}
scanf("%d%d%d%d%d",&p,&m,&f,&mm,&s);
for(i=1;i<=n;i++){
addedge(0,i,inf,p);
addedge(0,n+i,need[i],0);
addedge(i,2*n+1,need[i],0);
if(i+m<=n)addedge(n+i,i+m,inf,f);
if(i+mm<=n)addedge(n+i,i+mm,inf,s);
if(i+1<=n)addedge(n+i,n+i+1,inf,0);
}
long long cost;
mincostmaxflow(0,2*n+1,cost);
printf("%lld\n",cost);
//cout << "Hello world!" << endl;
return 0;
}
3/24 家园
题解:枚举答案,每次都在上一个答案的残量网络上跑网络流,用并查集检查能否到达,注意初始化并查集要初始化到n+2,否则会在第八个点出问题。
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=20005;//点数的最大值
const int MAXM=1000005;//边数的最大值
struct Node
{
int from,to,next;
int cap;
}edge[MAXM];
int tol;
int dep[MAXN];//dep为点的层次
int head[MAXN];
int n;
int p[25];
void init()
{
tol=0;
memset(head,-1,sizeof(head));
int i;
for(i=1;i<=n+3;i++)p[i]=i;//new
}
void addedge(int u,int v,int w)//第一条变下标必须为偶数
{
edge[tol].from=u;
edge[tol].to=v;
edge[tol].cap=w;
edge[tol].next=head[u];
head[u]=tol++;
edge[tol].from=v;
edge[tol].to=u;
edge[tol].cap=0;
edge[tol].next=head[v];
head[v]=tol++;
}
int BFS(int start,int end)
{
int que[MAXN];
int front,rear;
front=rear=0;
memset(dep,-1,sizeof(dep));
que[rear++]=start;
dep[start]=0;
while(front!=rear)
{
int u=que[front++];
if(front==MAXN)front=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(edge[i].cap>0&&dep[v]==-1)
{
dep[v]=dep[u]+1;
que[rear++]=v;
if(rear>=MAXN)rear=0;
if(v==end)return 1;
}
}
}
return 0;
}
int dinic(int start,int end)
{
int res=0;
int top;
int stack[MAXN];//stack为栈,存储当前增广路
int cur[MAXN];//存储当前点的后继
while(BFS(start,end))
{
memcpy(cur,head,sizeof(head));
int u=start;
top=0;
while(1)
{
if(u==end)
{
int min=INF;
int loc;
for(int i=0;i<top;i++)
if(min>edge[stack[i]].cap)
{
min=edge[stack[i]].cap;
loc=i;
}
for(int i=0;i<top;i++)
{
edge[stack[i]].cap-=min;
edge[stack[i]^1].cap+=min;
}
res+=min;
top=loc;
u=edge[stack[top]].from;
}
for(int i=cur[u];i!=-1;cur[u]=i=edge[i].next)
if(edge[i].cap!=0&&dep[u]+1==dep[edge[i].to])
break;
if(cur[u]!=-1)
{
stack[top++]=cur[u];
u=edge[cur[u]].to;
}
else
{
if(top==0)break;
dep[u]=-1;
u=edge[stack[--top]].from;
}
}
}
return res;
}
/////////////////////
int m,k;
int cap[25],num[25],g[25][25];
int get(int ans,int x){
if(x==n+2)return 20000;
return ans*(n+1)+x;
}
int finde(int x){
return x==p[x]?x:p[x]=finde(p[x]);
}
void join(int x,int y){
x=finde(x);
y=finde(y);
if(x==y)return ;
p[x]=y;
return ;
}
int main()
{
int i,j;
scanf("%d%d%d",&n,&m,&k);
init();
for(i=0;i<m;i++)
{
scanf("%d%d",&cap[i],&num[i]);
for(j=0;j<num[i];j++)
{
scanf("%d",&g[i][j]);
if(g[i][j]==-1)g[i][j]=n+2;//moon
else if(g[i][j]==0)g[i][j]=n+1;//earth
if(j>0)join(g[i][j],g[i][j-1]);
}
}
if(finde(n+1)!=finde(n+2)){
printf("0\n");
return 0;
}
int ans=0,flow=0,x,y;
int s=0,t=20000;
for(ans=1;flow<k;ans++){
if(ans>1)
{
for(i=1;i<=n+1;i++)addedge(get(ans-1,i),get(ans,i),INF);
}
addedge(s,get(ans-1,n+1),INF);
for(i=0;i<m;i++){
x=(ans-1)%num[i];
y=ans%num[i];
addedge(get(ans-1,g[i][x]),get(ans,g[i][y]),cap[i]);
}
flow+=dinic(s,t);
}
printf("%d\n",ans-1);
return 0;
}
代码: