hdu1565 网络流或状态压缩DP
对于网络流有一个定理:
最小点权覆盖集=最大网络流;
最大点权独立集=总权值-最小点权覆盖集;
网络流解法代码如下:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define N 1010 #define M 50010 #define inf 1<<30 using namespace std; struct Edge{ int to,val,next; }edge[M]; int index[N],d[N],gap[N],e; void addedge(int from,int to,int val) { edge[e].to=to; edge[e].val=val; edge[e].next=index[from]; index[from]=e++; edge[e].to=from; edge[e].val=0; edge[e].next=index[to]; index[to]=e++; } int source,des,n,m; //n is the number of point int dfs(int pos,int flow) { if(pos==des) return flow; int i,j,v,val,lv,mind,c; mind=n-1;//初始最小标号为n-1 lv=flow; for(i=index[pos];i!=-1;i=edge[i].next) { v=edge[i].to; val=edge[i].val; if(val) { if(d[v]+1==d[pos]) { c=min(lv,val);//对于该点的最小可行流 c=dfs(v,c); edge[i].val-=c;//更新剩余图 edge[i^1].val+=c; lv-=c; if(d[source]>=n)return flow-lv; if(lv==0) break; } if(d[v]<mind)mind=d[v];//找出与pos相连的点的最小标号 } } if(lv==flow)//没有找到增广路劲,进行标号更新 { --gap[d[pos]]; if(!gap[d[pos]]) d[source]=n; d[pos]=mind+1; ++gap[d[pos]]; } return flow-lv; } int sap(int st,int de) { source=st; des=de; memset(d,0,sizeof(d)); memset(gap,0,sizeof(gap)); gap[0]=n;//初始标号为0的有n个. int ans=0; while(d[st]<n) { ans+=dfs(st,inf); //cout<<d[st]<<endl; } return ans; } void init() { e=0; memset(index,-1,sizeof(index)); } int pos(int a,int b) { return (a-1)*m+b; } int main() { int t,i,j; while(scanf("%d",&m)!=EOF) { init(); int w; int sum=0; for(i=1;i<=m;i++) for(j=1;j<=m;j++) { scanf("%d",&w); sum+=w; if((i+j)%2==0) { addedge(0,pos(i,j),w); if(i<m) addedge(pos(i,j),pos(i+1,j),inf); if(j<m) addedge(pos(i,j),pos(i,j+1),inf); if(i>1) addedge(pos(i,j),pos(i-1,j),inf); if(j>1) addedge(pos(i,j),pos(i,j-1),inf); } else addedge(pos(i,j),m*m+1,w); } n=m*m+2; printf("%d\n",sum-sap(0,n-1)); } return 0; }
状态压缩解法:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int s[1<<21]; int map[22][22]; int dp[2][1<<21]; int main() { int n,i,j,t,k,num=0;; n=1<<21; for(i=0;i<=n;i++)//记录可行状态 if((i&(i<<1))==0) s[num++]=i; while(scanf("%d",&n)!=EOF) { for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%d",&map[i][j]); int p=0; memset(dp,0,sizeof(dp)); for(i=0;i<n;i++)//枚举每一行 { p^=1;//进行滚动数组 for(j=0;j<num;j++)//枚举每行的所有状态 { if(s[j]>(1<<n)) break; int sum=0; for(k=0;k<n;k++) if(s[j]&(1<<k)) sum+=map[i][k];//记录该状态值 for(k=0;k<num;k++)//枚举已经得到的状态转移到该状态能到的最大值 { if(s[k]>(1<<n)) break; if(!(s[k]&s[j])) dp[p][s[j]]=max(dp[p][s[j]],dp[1-p][s[k]]+sum); } } } int ans=0; for(i=0;i<num&&s[i]<=(1<<n);i++)//寻找答案 ans=max(ans,dp[p][s[i]]); printf("%d\n",ans); } return 0; }