王晓东《线性规划和网络流24题》
求最小点路径覆盖并打印
#include <cstdio> #include <cstring> using namespace std; const int maxn=450; const int inf=1<<25; const int s=0; int n,m; struct edge{ int v,next,w; }edge[maxn*maxn]; int head[maxn],cnt;//for sap void addedge(int u, int v, int w) { edge[cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].w=0; edge[cnt].next=head[v]; head[v]=cnt++; } int sap(int t) { int pre[maxn],cur[maxn]; int dis[maxn],gap[maxn]; int flow=0 , aug=inf ,u; bool flag; for (int i=0 ; i<=t ; ++i) { cur[i]=head[i]; gap[i]=dis[i]=0; } gap[s]=t+1; u=pre[s]=s; while (dis[s]<=t) { flag=0 ; for (int &j=cur[u] ; ~j ; j=edge[j].next) { int v=edge[j].v; if (edge[j].w>0 && dis[u]==dis[v]+1) { flag=1; if(edge[j].w<aug)aug=edge[j].w; pre[v]=u; u=v; if (u==t) { flow+=aug; while (u!=s) { u=pre[u]; edge[cur[u]].w-=aug; edge[cur[u]^1].w+=aug; } aug=inf; } break; } } if (flag)continue ; int mindis=t+1; for (int j=head[u]; ~j ; j=edge[j].next) { int v=edge[j].v; if (edge[j].w>0 && dis[v]<mindis) { mindis=dis[v]; cur[u]=j; } } if(--gap[dis[u]]==0)break; gap[dis[u]=mindis+1]++; u=pre[u]; } return flow; } ///////////// bool vis[maxn]; int path[maxn],pcnt; //保存路径并记录路径长度 void dfs (int u) { vis [u]=true; path[pcnt++]=u; for(int p=head[u] ; ~p ; p=edge[p].next) if(!vis[edge[p].v]) { if(edge[p].v) { if(edge[p].w==0)dfs(edge[p].v-n); } } } void build_graph() { for (int i=1 ; i<=n ; ++i) { addedge (0 , i , 1); addedge (i+n , 2*n+1 , 1); } } void init () { memset (head , -1 , sizeof(head)); cnt=0; } int main () { int u,v; freopen ("path5.in" , "r" , stdin); freopen ("out1.txt" , "w" , stdout); while (~scanf("%d%d",&n,&m)) { init(); for (int i=0 ; i<m ; ++i) { scanf("%d%d",&u,&v); addedge(u , v+n , 1); } build_graph(); int ans=sap(2*n+1); memset (vis , false , sizeof(vis)); for (int i=1 ; i<=n ; ++i) { pcnt=0; if(vis[i])continue; dfs(i); for (int j=0 ; j<pcnt ; ++j) printf("%d%c",path[j],j==pcnt-1?'\n':' '); } printf("%d\n",n-ans); } return 0; }
JOJ 2730 stock(torry唐牛出品)
给出n个k长的序列,问最少需要多少个图画出所有的序列,使每个图的序列不相交。
一开构图时根据2点之间不相交的关系连无向边,结果构出了一个有环无向图(囧)。
正确构图方法是将无向定义有向,并去环, 这样可以考虑使等价关系变成偏序关系,即互不相交变成当一个序列完全大于另一个序列时连一条有向边,这样就是标准的DAG图,再求下最小路径覆盖就好了。
#include <cstdio> #include <cstring> const int maxn=210; const int inf=1<<25; const int s=0; int n,m; struct edge{ int v,next,w; }edge[maxn*maxn]; int head[maxn],cnt;//for sap void addedge(int u, int v, int w) { edge[cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].w=0; edge[cnt].next=head[v]; head[v]=cnt++; } int sap(int t) { int pre[maxn],cur[maxn]; int dis[maxn],gap[maxn]; int flow=0 , aug=inf ,u; bool flag; for (int i=0 ; i<=t ; ++i) { cur[i]=head[i]; gap[i]=dis[i]=0; } gap[s]=t+1; u=pre[s]=s; while (dis[s]<=t) { flag=0 ; for (int &j=cur[u] ; ~j ; j=edge[j].next) { int v=edge[j].v; if (edge[j].w>0 && dis[u]==dis[v]+1) { flag=1; if(edge[j].w<aug)aug=edge[j].w; pre[v]=u; u=v; if (u==t) { flow+=aug; while (u!=s) { u=pre[u]; edge[cur[u]].w-=aug; edge[cur[u]^1].w+=aug; } aug=inf; } break; } } if (flag)continue ; int mindis=t+1; for (int j=head[u]; ~j ; j=edge[j].next) { int v=edge[j].v; if (edge[j].w>0 && dis[v]<mindis) { mindis=dis[v]; cur[u]=j; } } if(--gap[dis[u]]==0)break; gap[dis[u]=mindis+1]++; u=pre[u]; } return flow; } int stock[maxn][30]; int k; bool valid (int x, int y) { for (int i=0 ; i<k ; ++i) { if(stock[x][i]<=stock[y][i])return false ; } return true ; } void build_graph()//o(n*n*k) { memset (head , -1 , sizeof(head)); cnt=0; for (int i=0 ; i<n ; ++i) { addedge(0 , i+1 , 1); addedge(i+1+n , 2*n+1 , 1); for (int j=0 ; j<n ; ++j) { if(valid(i,j)) addedge(i+1 , j+1+n , 1); } } } int main () { int u,v; int cas; scanf("%d",&cas); for (int I=1 ; I<=cas ; ++I) { scanf("%d%d",&n,&k); for (int i=0 ; i<n ; ++i) { for (int j=0 ; j<k ; ++j) { scanf("%d",*(stock+i)+j); } } build_graph(); printf("Case #%d: %d\n",I,n-sap(2*n+1)); } return 0; }