flag待补全
6/21
提交地址:cogs
思路: 二分图最大匹配建图
代码:
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 105,inf = 0x3f3f3f3f;
struct node
{
int to,next,cap,rev;
node(){}
node(int a,int b,int c,int d){to = a; next = b; cap = c; rev = d;}
}edge[maxn*maxn<<1];
int h[maxn],lev[maxn];
int n,m,a,b;
int num,s,t;
void init()
{
for(int i = 0; i < maxn; i ++) h[i] = -1;
num = 0; s = 0; t = n+1;
}
void add(int u,int v)
{
edge[num] = node(v,h[u],1,num+1); h[u] = num++;
edge[num] = node(u,h[v],0,num-1); h[v] = num++;
}
bool bfs()
{
for(int i = s; i <= t; i++) lev[i] = -1;
lev[s] = 0;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = h[u] ;~i; i=edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap;
if(lev[v] == -1 && cap)
{
lev[v] = lev[u] + 1;
q.push(v);
}
}
}
return lev[t] != -1;
}
int dfs(int u,int f)
{
if(u == t) return f;
for(int i = h[u]; ~i ; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap, rev = edge[i].rev;
if(lev[v] == lev[u] + 1 && cap)
{
int k = dfs(v,min(cap,f));
if(!k) continue;
edge[i].cap -= k;
edge[rev].cap +=k;
return k;
}
}
lev[u] = -2;
return 0;
}
void dinic()
{
int flow = 0;
while(bfs())
{
while(int k = dfs(s,inf)) flow += k;
}
printf("%d\n",flow);
}
int main()
{
freopen("flyer.in","r",stdin);
freopen("flyer.out","w",stdout);
scanf("%d%d",&n,&m);
init();
for(int i = 1; i <= m; i++) add(s,i);
while(~scanf("%d%d",&a,&b))
{
add(a,b);
}
for(int i = m+1; i <= n; i++) add(i,t);
dinic();
return 0;
}
思路:
最大权闭合图,要求输出方案,最后选定的结点就是还存在于残量网络中的,也就是lev[i]还>0 的点
最大权闭合回路连图方案,对所有正权点连图S->v cap为v的值,而对所有负权点连边v->T,cap为负权取反,然后对所有的u>v连边,cap为inf,在这个图上跑最大流,最后的答案就为Sum(val[i]) (val[i]>0)-Max_flow;
代码:
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 300,inf = 0x3f3f3f3f;
struct node
{
int to,next,cap,ori,rev;
node(){}
node(int a,int b,int c,int d,int e){to = a; next = b; cap = c; rev = d;ori = e;}
}edge[maxn*maxn];
int h[maxn],lev[maxn];
int vis[maxn],used[maxn];
int num,n,m,s,t;
void init()
{
memset(h,-1,sizeof(h));
for(int i = 0; i <= max(n,m); i++) vis[i] = used[i] = 0;
num = 0;
s = 0, t = n+m+1;
}
void add(int u,int v,int flow)
{
edge[num] = node(v,h[u],flow,num+1,flow); h[u] = num++;
edge[num] = node(u,h[v], 0 , num-1, 0); h[v] = num++;
}
bool bfs()
{
for(int i = s; i <= t; i++) lev[i] = -1;
queue<int> q;
q.push(s); lev[s] = 0;
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = h[u] ;~i; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap;
//cout << v << endl;
if(lev[v] == -1 && cap)
{
lev[v] = lev[u] + 1;
q.push(v);
}
}
}
return lev[t] != -1;
}
int dfs(int u,int f)
{
if(u == t) return f;
for(int i = h[u] ;~i ; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap, rev = edge[i].rev;
if(lev[v] == lev[u] + 1 && cap)
{
int k = dfs(v,min(f,cap));
if(!k) continue;
edge[i].cap -= k;
edge[rev].cap += k;
return k;
}
}
lev[u] = -2;
return 0;
}
int Max_flow()
{
int flow = 0;
while(bfs())
while(int k = dfs(s,inf)) flow += k;
return flow;
}
char str[1005];
int main()
{
freopen("shuttle.in","r",stdin);
freopen("shuttle.out","w",stdout);
int a,b,c;
while(~scanf("%d%d",&n,&m))
{
init();
int sum = 0;
cin.getline(str,1005);
for(int i = 1; i <= n; i++)
{
cin.getline(str,1005);
stringstream ss;int temp;
ss << str; ss >> temp;
sum += temp;
add(s,i,temp);
while(ss >> temp)
add(i, temp+n , inf);
}
for(int i = 1; i <= m; i++)
scanf("%d",&c),add(i+n,t,c);
int cnt = Max_flow();
for(int i = 1; i <= n; i++) if(lev[i] > 0) vis[i] = 1;
for(int i = 1+n; i <= n+m; i++) if(lev[i] > 0) used[i-n] = 1;
int flag = 0,mark = 0;
for(int i = 1; i <= n; i++)
{
if(vis[i])
{
if(flag) cout << " ";
cout << i;
flag = 1;
}
}
cout << endl;
for(int i = 1; i <= m; i++)
{
if(used[i] == 1)
{
if(mark) cout <<" ";
cout << i;
mark = 1;
}
}
cout << endl;
printf("%d\n", sum - cnt);
}
return 0;
}
思路:
题目给出了思路….
对于求(DAG上的)最小路径覆盖问题,对所有点拆点,然后连边s->v(1->n), cap为1, v+n ->t(1->n),在图中有边的则连边 u->v+n,这里所有边的cap都为1。最后答案为n-Max_flow
这个题目要求输出路径,这里明显匹配了的边都是选定的边,所有我们只需要从头到尾找i->j,j->k,这种就够了。
代码:
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 300 + 5, maxe = maxn*maxn,inf = 0x3f3f3f3f;
struct node
{
int to,flow,rev,next;
node(){}
node(int a,int b,int c,int d){to = a;flow = b; rev = c; next = d;}
node(int a,int b){to = a;next = b;}
}edge[maxe];
int h[maxn],e[maxn],vis[maxn];
int lev[maxn];
int num,m,n,s,t,edgenum;
void init()
{
num = edgenum = 0;
for(int i = 0; i <= 2*n + 1; i++)
h[i] = -1,e[i] = -1,vis[i] = 0;
s = 0, t = 2*n+1;
}
void add(int u,int v,int cap)
{
edge[num] = node(v,cap,num+1,h[u]); h[u] = num++;
edge[num] = node(u,0,num-1,h[v]); h[v] = num++;
}
bool bfs()
{
for(int i = s; i <= t; i++) lev[i] = -1;
queue<int> q;
q.push(s); lev[s] = 0;
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = h[u];~i;i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].flow;
if(lev[v] == -1 && cap)
{
lev[v] = lev[u] + 1;
q.push(v);
}
}
}
return lev[t] != -1;
}
int dfs(int u,int f)
{
if(u ==t )return f;
for(int i = h[u] ;~i; i =edge[i].next)
{
int v = edge[i].to, flow = edge[i].flow,rev = edge[i].rev;
if(lev[v] == lev[u] + 1 && flow)
{
int k = dfs(v, min (f,flow));
if(!k) continue;
edge[i].flow -= k;
edge[rev].flow += k;
return k;
}
}
lev[u] = -2;
return 0;
}
void Find(int u,int ori)
{
vis[u] = 1;
if(u == ori) cout << u;
else cout << " " << u;
if(e[u] != -1) Find(e[u],ori);
}
void Max_flow()
{
int flow = 0,cnt = 0;
while(bfs())
while(int k = dfs(s,inf)) flow += k;
//cout << flow << endl;
for(int u = 1; u <= n; u++)
for(int i = h[u] ; ~i; i = edge[i].next)
{
int v = edge[i].to, flow = edge[i].flow;
if(v <= 2*n && v > n && flow == 0) e[u] = v-n;
}
for(int i = 1; i <= n; i++)
{
if(!vis[i]) {cnt++,Find(i,i);cout << endl;}
}
printf("%d\n",cnt);
}
int main()
{
freopen("path3.in","r",stdin);
freopen("path3.out","w",stdout);
int a,b,c;
while(~scanf("%d%d",&n,&m))
{
init();
for(int i = 1; i <= n; i++) add(s,i,1);
for(int i = 0; i < m; i++)
scanf("%d%d",&a,&b),add(a,b+n,1);
for(int i = n+1 ; i <= n*2 ; i++)
add(i,t,1);
Max_flow();
}
return 0;
}
思路: 可以看作i与j有边(满足i+j为一个完全平方数),所以这个题也可以看作上一题的最小路径覆盖问题。
代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 3205,maxe = 1e6,inf = 0x3f3f3f3f;
struct node
{
int to,cap,rev,next;
node(){}
node(int a,int b,int c,int d){to = a; cap = b; rev = c; next = d;}
}edge[maxe<<1];
int num,s,t,n;
int h[maxn],lev[maxn];
int ans[61];
void add(int u,int v,int cap)
{
edge[num] = node(v,cap,num+1,h[u]); h[u] = num++;
edge[num] = node(u,0,num - 1,h[v]); h[v] = num++;
}
void init()
{
for(int i = 0; i < maxn; i++) h[i] = -1;
s = num = 0;
}
bool bfs()
{
for(int i = 0; i <= t; i++) lev[i] = -1;
queue<int> q;
q.push(s); lev[s] = 0;
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = h[u] ;~i; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap;
if(lev[v] == -1 && cap)
{
lev[v] = lev[u] + 1;
q.push(v);
}
}
}
return lev[t] != -1;
}
int dfs(int u,int f)
{
if(u == t) return f;
for(int i = h[u] ;~i; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap, rev = edge[i].rev;
if(lev[v] == lev[u] + 1 && cap)
{
int k = dfs(v,min(cap,f));
if(!k) continue;
edge[i].cap -= k;
edge[rev].cap += k;
return k;
}
}
lev[u] = -2;
return 0;
}
int Max_flow()
{
int flow = 0;
while(bfs())
{
while(int k = dfs(s,inf)) flow += k;
}
return flow;
}
int main()
{
freopen("balla.in","r",stdin);
freopen("balla.out","w",stdout);
t = 1600*2+1;
init();
int temp=0;
for(int i = 1; i <= 1600; i++)
{
add(s,i,1); add(i+1600,t,1);
for(int j = 1; j < i; j++)
{
int th = sqrt(i+j);
if(th*th == (i+j)) add(j,i+1600,1);
}
temp += Max_flow();
ans[i-temp] = i;
}
while(~scanf("%d",&n))
printf("%d\n",ans[n]);
return 0;
}
思路: 这个感觉就是直接连边……最后找一找残量网络中那些由不同单位到餐桌上flow为0的边,这代表这个单位到这个餐桌有一个代表。
代码:
#include
#include
#include
#include
#include
using namespace std;
const int N = 150+5, M = 270+5, maxn = N+M, inf = 0x3f3f3f3f;
struct node
{
int to,next,cap,rev;
node(){}
node(int a,int b,int c,int d){to = a; next = b; cap = c; rev = d;}
}edge[maxn*maxn];
//int ans[maxn];
int lev[maxn];
int h[maxn];
int num,s,t, n,m,sum;
void init()
{
for(int i = 0; i < (n+m)+2; i++)
h[i] = -1;
num = 0;
s = 0, t = n+m+1; sum = 0;
}
bool bfs()
{
for(int i = s; i <= t; i++) lev[i] = -1;
lev[0] = 0;
queue<int> q;
q.push(0);
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = h[u]; ~i; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap;
if(lev[v] == -1 && cap)
{
lev[v] = lev[u] + 1;
q.push(v);
}
}
}
return lev[t] != -1;
}
int dfs(int u,int f)
{
if(u==t) return f;
for(int i = h[u]; ~i ; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap, rev = edge[i].rev;
if(lev[v] == lev[u] + 1 && cap)
{
int k = dfs(v,min(f,cap));
if(!k) continue;
edge[i].cap -= k;
edge[rev].cap += k;
return k;
}
}
lev[u] = -2;
return 0;
}
void add(int u,int v,int cap)
{
edge[num] = node(v,h[u],cap,num+1); h[u] = num++;
edge[num] = node(u,h[v],0,num-1); h[v] = num++;
}
void solve()
{
int flow = 0;
while(bfs())
while(int k = dfs(0,inf)) flow += k;
if(flow != sum) printf("0\n");
else
{
printf("1\n");
for(int u = 1; u <= n; u++)
{
int flag = 0;
for(int i = h[u] ;~i; i = edge[i].next)
{
int v = edge[i].to, cap = edge[i].cap;
if(cap == 0)
{
if(flag) printf(" ");
printf("%d",v-n);
flag = 1;
}
}
printf("\n");
}
}
}
int main()
{
freopen("roundtable.in","r",stdin);
freopen("roundtable.out","w",stdout);
int a;
while(~scanf("%d%d",&n,&m))
{
init();
for(int i = 1; i <= n; i++) scanf("%d",&a), add(s,i,a), sum += a;
for(int i = 1; i <= m; i++) scanf("%d",&a), add(n+i,t,a);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
add(i,n+j,1);
solve();
}
return 0;
}
思路: 这个连边就是直接连,然后求一遍最小费用流,我在求最大费用的时候是把d[i]置成-inf,然后求的最长路。
代码:
//求最大费用流与最小费用流
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
const int inf = 0x3f3f3f3f,maxn = 200 + 10,maxe = 2e4 + 10;
struct node
{
int to,cap,w,rev,next,from;
node(){}
node(int a,int b,int c,int d,int e,int f)
{to = a; cap = b; w = c; rev = d; next = e;from = f;}
}edge[maxe<<1],edge1[maxe << 1];
int h[maxn],h1[maxn],d[maxn],a[maxn],inq[maxn],pre[maxn];
int num,num1,s,t,n,m;
void init()
{
s = num = num1 = 0; t = n+m+1;
for(int i = 0; i < maxn; i++) h[i] = h1[i] = -1;
}
void add(int u,int v,int cap,int w)
{
edge[num] = node(v,cap,w,num+1,h[u],u); h[u] = num++;
edge[num] = node(u,0,-w,num-1, h[v],v); h[v] = num++;
edge1[num1] = node(v,cap,w,num1+1,h1[u],u); h1[u] = num1++;
edge1[num1] = node(u,0,-w,num1-1, h1[v],v); h1[v] = num1++;
}
int bfs()
{
for(int i = 0; i < maxn ; i++)
d[i] = inf, a[i] = 0, inq[i] = 0;
queue<int> q;
q.push(0); d[0] = 0, a[0] = inf, inq[0] = 1;
while(!q.empty())
{
int u = q.front(); q.pop();
inq[u] = 0;
for(int i = h[u] ;~i; i = edge[i].next)
{
int v = edge[i].to, cap = edge[i].cap,w = edge[i].w;
if(cap && d[v] > d[u] + w)
{
d[v] = d[u] + w;
a[v] = min(a[u], cap);
pre[v] = i;
if(!inq[v]){q.push(v); inq[v] = 1;}
}
}
}
if(d[t] == inf) return 0;
for(int u = t; u != s; u = edge[pre[u]].from)
{
int rev = edge[pre[u]].rev;
edge[pre[u]].cap -= a[t];
edge[rev].cap += a[t];
}
return d[t]*a[t];
}
int bfs1()
{
for(int i = 0; i < maxn ; i++)
d[i] = -inf, a[i] = 0, inq[i] = 0;
queue<int> q;
q.push(0); d[0] = 0, a[0] = inf, inq[0] = 1;
while(!q.empty())
{
int u = q.front(); q.pop();
inq[u] = 0;
for(int i = h1[u] ;~i; i = edge1[i].next)
{
int v = edge1[i].to, cap = edge1[i].cap , w = edge1[i].w;
if(cap && d[v] < d[u] + w)
{
d[v] = d[u] + w;
a[v] = min(a[u], cap);
pre[v] = i;
if(!inq[v]){q.push(v); inq[v] = 1;}
}
}
}
if(d[t] == -inf) return 0;
for(int u = t; u != s; u = edge1[pre[u]].from)
{
int rev = edge1[pre[u]].rev;
edge1[pre[u]].cap -= a[t];
edge1[rev].cap += a[t];
}
return d[t]*a[t];
}
void E_K1()
{
ll res = 0;
while(int k = bfs()) res += k;
printf("%lld\n",res);
}
void E_K2()
{
ll res = 0;
while(int k = bfs1()) res += k;
printf("%lld\n",res);
}
int main()
{
freopen("tran.in","r",stdin);
freopen("tran.out","w",stdout);
int a;
scanf("%d%d",&n,&m);
init();
for(int i = 1; i <= n; i++)scanf("%d",&a), add(s,i,a,0);
for(int i = 1; i <= m; i++)scanf("%d",&a), add(i+n,t,a,0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)scanf("%d",&a),add(i,j+n,inf,a);
E_K1();
E_K2();
return 0;
}