C HDU 3488
有N个城市和M条带权的有向的路,要求设计多条闭合的环状线路,使得每个城市均只被其中的一条线路经过一次(线路的初始点除外),并且所有线路经过的路的权值之和最小。
对于有向圈上的每一个点,它们的出度和入度都一定为1。因此将每个点拆分为两个点,做最大权最大匹配(因为题目要求权值最小,所以把边权变为负数做,最后再把答案乘上-1),得到的结果就是答案了。(我完全没想到这么做。。)
#include<cstdio> #include<cstring> #include<algorithm> #define maxn 205 #define inf 99999999 using namespace std; int vis_g[maxn],vis_b[maxn],ex_g[maxn],ex_b[maxn],slack[maxn]; int n,mp[maxn][maxn],match[maxn]; int dfs(int x) { int i,tmp; vis_g[x]=1; for (i=1;i<=n;i++) { if (vis_b[i]) continue; tmp=ex_g[x]+ex_b[i]-mp[x][i]; if (tmp==0) { vis_b[i]=1; if (!match[i] || dfs(match[i])) { match[i]=x; return 1; } } else slack[i]=min(slack[i],tmp); } return 0; } int km() { int i,j,re=0,tmp; for (i=1;i<=n;i++) { ex_g[i]=0; ex_b[i]=0; match[i]=0; for (j=1;j<=n;j++) ex_g[i]=max(ex_g[i],mp[i][j]); } for (i=1;i<=n;i++) { for (j=1;j<=n;j++) slack[j]=inf; while (1) { memset(vis_g,0,sizeof(vis_g)); memset(vis_b,0,sizeof(vis_b)); if (dfs(i)) break; tmp=inf; for (j=1;j<=n;j++) if (!vis_b[j]) tmp=min(tmp,slack[j]); for (j=1;j<=n;j++) { if (vis_g[j]) ex_g[j]-=tmp; if (vis_b[j]) ex_b[j]+=tmp; else slack[j]-=tmp; } } } for (i=1;i<=n;i++) re+=mp[match[i]][i]; return re; } int main() { int i,j,T,m,x,y,z; scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); for (i=1;i<=n;i++) for (j=1;j<=n;j++) mp[i][j]=-inf; while (m--) { scanf("%d%d%d",&x,&y,&z); if (-z<mp[x][y]) continue; mp[x][y]=-z; } printf("%d ",-km()); } return 0; }
AC自动机+DFS序树状数组更新答案。()
AC自动机+DP+矩阵快速幂。()
将两端颜色看作点,中间的权值看作边权,则题中所给的N个block可以看作N条边。
题目就变成了求给定图中边权之和最大的欧拉通路或欧拉回路(不是严谨的欧拉通路/回路,因为并不一定要经过图中的每一条边)。
而只有图中度为奇数的点的数量等于2时,图中才会存在欧拉通路;只有图中所有顶点的度均为偶数时,图中才会存在欧拉回路。因此图中的奇度顶点数必须小于等于2。
由于题目所给的图中最多只有四个顶点(四种颜色),所以奇度顶点数最多为4,这时只要删去一条边就可满足欧拉通路/回路出现的条件。
于是就枚举每一条边删去的情况,检查图中奇度顶点数是否小于等于2,dfs得到欧拉通路/回路的边权之和,更新答案。
#include<cstdio> #include<cstring> #define maxn 5 #define maxm 205 using namespace std; int num,last[maxn],vis[maxm],used[maxn],deg[maxn],re; struct edge { int to,from,nxt,w; }e[maxm]; void add(int x,int y,int z) { e[++num].to=y; e[num].from=x; e[num].w=z; e[num].nxt=last[x]; last[x]=num; } void dfs(int u) { used[u]=1; for (int i=last[u];i!=-1;i=e[i].nxt) { if (!vis[i] && !vis[i^1]) { vis[i]=1; dfs(e[i].to); re+=e[i].w; } } } int main() { int i,j,k,n,x,y,z,cnt,ans; while (scanf("%d",&n)!=EOF) { num=-1;ans=0; memset(last,-1,sizeof(last)); memset(deg,0,sizeof(deg)); for (i=1;i<=n;i++) { scanf("%d%d%d",&x,&z,&y); add(x,y,z); add(y,x,z); deg[x]++; deg[y]++; } for (i=0;i<=num+1;i++)//num+1为不删边的情况 { if (i!=num+1 && e[i].from==e[i].to) continue; if (i!=num+1) { deg[e[i].from]--; deg[e[i].to]--; } for (j=1;j<=4;j++) { memset(used,0,sizeof(used)); memset(vis,0,sizeof(vis)); if (i!=num+1) vis[i]=1; re=0;cnt=0; dfs(j); for (k=1;k<=4;k++) if (used[k] && deg[k]%2==1) cnt++; if (cnt<=2 && ans<re) ans=re; } if (i!=num+1) { deg[e[i].from]++; deg[e[i].to]++; } } printf("%d ",ans); } return 0; }
I HDU 2435
J HDU 4322
巧妙建图+最大费用最大流。题解见:https://www.cnblogs.com/wally/archive/2013/08/28/3287980.html
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> #define maxn 50 #define maxm 5009 #define inf 99999999 using namespace std; int num,lst[maxn]; int pre[maxn],from[maxn],dis[maxn],vis[maxn],flow[maxn]; int n,m,k,sum,s,t,like[maxn][maxn],b[maxn]; struct in { int to,nxt,re,w; }e[maxm]; queue<int>q; void add(int x,int y,int z,int w) { e[++num].to=y;e[num].nxt=lst[x];lst[x]=num;e[num].re=z;e[num].w=w; e[++num].to=x;e[num].nxt=lst[y];lst[y]=num;e[num].re=0;e[num].w=-w; } int spfa() { int i,u,v; while (!q.empty()) q.pop(); for (i=0;i<=t;++i) { pre[i]=-1;//用于记录上一条边 from[i]=-1;//用于记录上一个点 dis[i]=-inf; vis[i]=0; } q.push(s); dis[s]=0; flow[s]=inf; vis[s]=1; while (!q.empty()) { u=q.front(); q.pop(); vis[u]=0; for (i=lst[u];i!=-1;i=e[i].nxt) { v=e[i].to; if (!e[i].re) continue; if (dis[v]<dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; flow[v]=min(e[i].re,flow[u]); pre[v]=i; from[v]=u; if (!vis[v]) { vis[v]=1; q.push(v); } } } } return pre[t]!=-1; } int mfmv() { int mincost=0,maxflow=0,v; while (spfa()) { //printf(" mincost %d and maxflow %d ",mincost,maxflow); mincost+=flow[t]*dis[t]; maxflow+=flow[t]; v=t; while (v!=s) { e[pre[v]].re-=flow[t]; e[pre[v]^1].re+=flow[t]; v=from[v]; } } //printf(" mincost %d and maxflow %d ",mincost,maxflow); return n-maxflow>=sum-mincost; } int main() { int ti,T,i,j; scanf("%d",&T); for (ti=1;ti<=T;ti++) { scanf("%d%d%d",&n,&m,&k); num=-1;sum=0; s=0;t=n+m+1; memset(lst,-1,sizeof(lst)); for (i=1;i<=m;i++) { scanf("%d",&b[i]); sum+=b[i]; } for (i=1;i<=n;i++) add(s,i,1,0); for (i=1;i<=m;i++) { for (j=1;j<=n;j++) { scanf("%d",&like[i][j]); if (like[i][j]==1) add(j,i+n,1,0); } add(i+n,t,b[i]/k,k); if (b[i]%k>1) add(i+n,t,1,b[i]%k); } printf("Case #%d: ",ti); if (mfmv()) printf("YES "); else printf("NO "); } return 0; }