codevs1227:方格取数2
题目描述 Description 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大 输入描述 Input Description 第一行两个数n,k(1<=n<=50, 0<=k<=10) 接下来n行,每行n个数,分别表示矩阵的每个格子的数 输出描述 Output Description 一个数,为最大和 样例输入 Sample Input 3 1 1 2 3 0 2 1 1 4 2 样例输出 Sample Output 11 数据范围及提示 Data Size & Hint 1<=n<=50, 0<=k<=10
芒果君:刚学的拆点,为数不多的自己写出来的网络流……理解题意后我们发现有以下几个要点:进行K次增广路;只能向右向下走;经过格点但可以不取数。那我们可以把每个格点拆成无权和有权的两个点。从其他点(同一位置有两个)走到该点,就走到无权点,流量视为inf,费用为0;当然也可以从同一点的无权走向有权,就是取出该数,只能取一次,流量是1,费用为负点权。左上连源点,右下连汇点。套KM的板子,最后的费用取相反数就是最大价值。注意建反边的费用是正边的相反数!
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<vector> 7 #include<map> 8 #include<set> 9 #include<queue> 10 #include<stack> 11 #include<bitset> 12 #include<string> 13 #define maxn 20010 14 using namespace std; 15 typedef long long ll; 16 const int inf=1<<29; 17 queue<int>Q; 18 int n,cnt,hl[maxn],s,t,k,vis[maxn],dis[maxn],pre[maxn],flow[maxn],id[maxn],tot; 19 inline int cal(int x,int y){return (x-1)*n+y;} 20 struct Edge{ 21 int u,v,c,f,ne,ctr; 22 }e[200010]; 23 void add(int u,int v,int c,int f,int ctr) 24 { 25 e[++cnt].u=u; 26 e[cnt].v=v; 27 e[cnt].c=c; 28 e[cnt].f=f; 29 e[cnt].ctr=cnt+ctr; 30 e[cnt].ne=hl[u]; 31 hl[u]=cnt; 32 } 33 bool bfs() 34 { 35 for(int i=0;i<=n*n*2+1;++i) dis[i]=inf,pre[i]=-1,vis[i]=0; 36 dis[s]=pre[s]=0; 37 flow[s]=inf; 38 Q.push(s); 39 while(!Q.empty()){ 40 int x=Q.front(); 41 Q.pop(); 42 vis[x]=0; 43 for(int i=hl[x];i;i=e[i].ne){ 44 int v=e[i].v,c=e[i].c,w=e[i].f; 45 if(c&&dis[v]>dis[x]+w){ 46 dis[v]=dis[x]+w; 47 pre[v]=x; 48 id[v]=i; 49 flow[v]=min(flow[x],c); 50 if(!vis[v]){ 51 vis[v]=1; 52 Q.push(v); 53 } 54 } 55 } 56 } 57 return dis[t]<inf; 58 } 59 int KM() 60 { 61 int ret(0); 62 while(bfs()){ 63 tot++; 64 if(tot>k) break; 65 int now=t; 66 while(now!=s){ 67 int i=id[now],j=e[i].ctr; 68 e[i].c-=flow[t]; 69 e[j].c+=flow[t]; 70 now=pre[now]; 71 } 72 ret+=flow[t]*dis[t]; 73 } 74 return ret; 75 } 76 int main() 77 { 78 int w; 79 scanf("%d%d",&n,&k); 80 for(int i=1;i<=n;++i) 81 for(int j=1;j<=n;++j){ 82 scanf("%d",&w); 83 add(cal(i,j),n*n+cal(i,j),1,-w,1); 84 add(n*n+cal(i,j),cal(i,j),0,w,-1); 85 } 86 puts(""); 87 for(int i=1;i<=n;++i) 88 for(int j=1;j<=n;++j){ 89 if(j+1<=n){ 90 add(cal(i,j),cal(i,j+1),inf,0,1); 91 add(cal(i,j+1),cal(i,j),0,0,-1); 92 add(n*n+cal(i,j),cal(i,j+1),1,0,1); 93 add(cal(i,j+1),n*n+cal(i,j),0,0,-1); 94 } 95 if(i+1<=n){ 96 add(cal(i,j),cal(i+1,j),inf,0,1); 97 add(cal(i+1,j),cal(i,j),0,0,-1); 98 add(n*n+cal(i,j),cal(i+1,j),1,0,1); 99 add(cal(i+1,j),n*n+cal(i,j),0,0,-1); 100 } 101 } 102 s=0,t=n*n*2+1; 103 add(0,1,inf,0,1);add(1,0,0,0,-1); 104 add(n*n,t,inf,0,1);add(t,n*n,0,0,-1); 105 add(t-1,t,inf,0,1);add(t,t-1,0,0,-1); 106 printf("%d\n",-KM()); 107 return 0; 108 }