赞
踩
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=37522548(MCMF)
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=37514575(状压dp)
本来就是一个二分图最大权匹配的KM板子题,上了板子就过了。
但是,看到了一个高中生的状压dp做法,
不得不说,有些高中生真的认真,代码通俗易懂可读性强,
的确是一步一个脚印扎扎实实写的代码(嗯白膜法师这种已经不能叫高中生了)
所以,借着人家的代码学一学从没学过的状压dp
QAQ睡个觉果然读代码能力强了不少,以后看不懂代码要及时休息啊,效率++
补一下MCMF的题解
按原来的样子建图,
二分图部分,每条边,费用=强度提升值,流量=1,
从每个左侧顶点流出来的流量就是1,只能匹配一个
超级源点s连左侧顶点,右侧顶点连超级汇点t,这些边,费用=0,流量=1
跑一遍最大费用最大流,费用即为总强度提升的部分
可以边权取负,跑最小费用最大流,最后再把tot的负再取回来
也可以每次spfa增广的时候找最长路,很灵活吖
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
-
- int a[110][110];
- int dp[1 << 15];
-
- int main()
- {
- int T; scanf("%d", &T);
- while(T--)
- {
- int n; scanf("%d", &n);
- for(int i = 0; i < n; i++)
- for(int j = 0; j < n; j++) scanf("%d", &a[i][j]);
- memset(dp, 0, sizeof(dp));
- for(int S = 1; S < (1 << n); S++)
- {
- int tot = 0;
- for(int i = 0; i < n; i++) tot += ((S >> i) & 1);
- tot--;
- for(int i = 0; i < n; i++)
- {
- if((S >> i) & 1)
- dp[S] = std::max(dp[S], dp[S ^ (1 << i)] + a[tot][i]);
- }
- }
- printf("%d\n", dp[(1 << n) - 1]);
- }
- return 0;
- }

先把人家的代码粘过来。
状压dp是二进制枚举思想,用第i位来枚举第i件物品,
1代表该物品已被选,不能再选了;0代表该物品尚未被选。
tot是枚举的二进制数中的1的个数,第tot个人可以在尚未被选的里面选一个。
为了与下标对应,tot减1。
核心句
if((S >> i) & 1)dp[S] = std::max(dp[S], dp[S ^ (1 << i)] + a[tot][i]);
感觉这个很形象啊QAQ,
第tot个人知道,当他选第i件商品的时候,能达成tot个人之和的最优,
于是他让前tot-1个人别选第i件商品了,“都让让都让让,你们从剩下tot-1件商品里挑,把第i件给我"。
我写博客咋变成这个画风了
然而我们不知道第tot个人选哪件能达成最优,
所以我们就要在状压的某状态(如01010101)的某个1值里,枚举一件给第tot个人。
即n个人分n件商品的最优,一定是(n-1)个人分其中(n-1)个商品的最优,加上最后一个人选剩下一件商品所求得。
状压dp,好就好在当第i位为1时,把第i位变0原值会变小,所以大一定从小转移而来。
因为有这个最优子结构的性质,tot个人的最优,一定是由tot-1个人的某种最优选法转移而来,故有等式成立。
如11可以由01或10转移而来,
前一种转移代表第一个人选了第二件而第二个人选了第一件,
后一种转移代表第一个人选了第一件而第二个人选了第二件,
但是既然这两种已经被前两个人选了,我们只用11保留这两种情况的最大值即最优情况。
其实这就跟递归一样,你不知道某个状态的值,甚至其子状态的值,
但是最底层的值和转移方程你是知道的,所以你就知道了一切.jpg。
状压dp一般很明显,O(2^n)所以一般n不超过15。
当然KM算法O(n^3)在这里显得优了好多。
只是学习一波状压dp罢了。
传说中的dp一题能想一晚上真不是吹的啊
要学会建图和改板子啊,毕竟抄了这么一个大板子
- #include<bits/stdc++.h>
- typedef long long ll;
- using namespace std;
- const int maxn=35;//点的数量
- const int N=2*maxn;
- const int maxm=5000;//2*maxn*maxn+4*maxn 源点出maxn条 汇点入maxn条 中间点两两连
- const ll INF=1e18;
- struct edge
- {
- int u,v;
- ll cos,nex,w;
- }e[maxm];
- ll vis[N],dis[N];
- ll cost[maxn][maxn];
- int head[N],cnt;
- void add(int u,int v,ll w,ll cos)
- {
- e[cnt].v=v;
- e[cnt].w=w;
- e[cnt].cos=cos;
- e[cnt].nex=head[u];
- head[u]=cnt++;
- }
- void add2(int u,int v,ll w,ll cos)
- {
- add(u,v,w,cos);
- add(v,u,0,-cos);
- }
- bool spfa(int s,int t)
- {
- for(int i=0;i<2*maxn;++i)
- dis[i]=INF;
- memset(vis,0,sizeof vis);
- vis[t]=1;
- dis[t]=0;
- deque<int>q;
- q.push_back(t);
- while(!q.empty())
- {
- int u=q.front();
- vis[u]=0;
- q.pop_front();
- for(int i=head[u];~i;i=e[i].nex)
- {
- int v=e[i].v;
- if(e[i^1].w>0&&dis[v]>dis[u]-e[i].cos)
- {
- dis[v]=dis[u]-e[i].cos;
- if(!vis[v])
- {
- vis[v]=1;
- if(!q.empty()&&dis[v]<dis[q.front()])
- q.push_front(v);
- else q.push_back(v);
- }
- }
- }
- }
- return dis[s]<INF;
- }
- ll dfs(int u,int t,ll low,ll &tot)
- {
- vis[u]=1;
- if(u==t)return low;
- ll ans=0;
- for(int i=head[u];~i;i=e[i].nex)
- {
- int v=e[i].v;
- if(!vis[v]&&e[i].w&&dis[v]==dis[u]-e[i].cos)
- {
- ll now=dfs(v,t,min(low,e[i].w),tot);
- if(now>0)
- {
- tot+=e[i].cos*now;
- low-=now;
- ans+=now;
- e[i].w-=now;
- e[i^1].w+=now;
- if(low==0)break;
- }
- }
- }
- return ans;
- }
- ll max_flow(int s,int t,ll &tot)//tot返回费用 函数返回值为最大流
- {
- ll ans=0;
- while(spfa(s,t))
- {
- vis[t]=1;
- while(vis[t])
- {
- memset(vis,0,sizeof vis);
- ans+=dfs(s,t,INF,tot);
- }
- }
- return ans;
- }
- void init()
- {
- cnt=0;
- for(int i=0;i<2*maxn;++i)
- dis[i]=INF;
- memset(head,-1,sizeof head);
- memset(vis,0,sizeof vis);
- }
- int main()
- {
- ll tot;//总费用
- int T,n,s,t;
- scanf("%d",&T);
- while(T--)
- {
- tot=0;
- init();
- scanf("%d",&n);
- //超级源 超级汇
- s=0;t=2*n+1;
- //左供货 右需求
- for(int i=1;i<=n;++i)
- add2(s,i,1,0);//超级源点
- for(int i=1;i<=n;++i)
- add2(n+i,t,1,0);//超级汇点
- for(int i=1;i<=n;++i)
- {
- for(int j=1;j<=n;++j)
- {
- scanf("%lld",&cost[i][j]);
- add2(i,n+j,1,-cost[i][j]);//l[i]的流量 dis的费用
- }
- }
- max_flow(s,t,tot);
- printf("%lld\n",-tot);
- }
- return 0;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。