hdu4862:http://acm.hdu.edu.cn/showproblem.php?pid=4862
题意:给你n*m的方格,每个方格中有一个数(0---9),然后你每次可以选择一个点开始,这个点是之前没有访问过的,然后你可以向右边的的方格跳,也可以向下面的方格跳。跳的过程中,你会丢失(x2-x1)+(y2-y1)-1一些能量。如果下一个方格和现在的方格的数字相同,那么你可以得到一些能量。每次一开始,你就可以随便跳。但是你最多只能选择k次开始。为你得到的最多能量,并且每个格子只访问一次。
题解:剖析一下,就知道,这一题是求最小k条路径覆盖(如果把边权取负的话)。接下是如果求解这个模型。建图方式如下。
把原图中点分成两部分X(n*m个),Y(n*m个),原图中的边(u,v,w),改成(u,v+n*m,-w);然后源点与X部分的每一点建一条边(st,x,0),然后st与p之间建一条边(st,p,0),容量为k,然后p与Y的每一点建一条边,然后Y与汇点建立一条边,这些变的容量都是1,费用都是0.然后跑最小费用最大流,如果满流则能跑完每个格子一次。否则不能。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #define inf 100000000 7 using namespace std; 8 const int E=1000000; 9 const int N=10002; 10 struct{ 11 int v, cap, cost, next; // re记录逆边的下标。 12 }edge[E]; 13 int n, m, ans,flow; 14 int k, cnt,head[N]; 15 int que[N], pre[N], dis[N]; 16 bool vis[N]; 17 char mp[12][12]; 18 void init(){//初始化 19 cnt=ans=0,flow=0; 20 memset(head,-1,sizeof(head)); 21 } 22 void addEdge(int u, int v, int ca, int co){ 23 edge[cnt].v = v; 24 edge[cnt].cap = ca; 25 edge[cnt].cost = co; 26 edge[cnt].next = head[u]; 27 head[u] = cnt ++; 28 edge[cnt].v = u; 29 edge[cnt].cap = 0; 30 edge[cnt].cost = -co; 31 edge[cnt].next = head[v]; 32 head[v] = cnt++; 33 } 34 bool spfa(){ // 源点为0,汇点为n。 35 int i; 36 for(i = 0; i <= 2*m*n+2; i ++){ 37 dis[i] = inf; 38 vis[i] = false; 39 } 40 queue<int>Q; 41 Q.push(2*n*m+1); 42 dis[2*n*m+1]=0; 43 vis[2*n*m+1] = true; 44 while(!Q.empty()){ // 这里最好用队列,有广搜的意思,堆栈像深搜。 45 int u = Q.front(); 46 Q.pop(); 47 for(i = head[u]; i != -1; i = edge[i].next){ 48 int v = edge[i].v; 49 if(edge[i].cap && dis[v] > dis[u] + edge[i].cost){ 50 dis[v] = dis[u] + edge[i].cost; 51 pre[v] = i; 52 if(!vis[v]){ 53 vis[v] = true; 54 Q.push(v); 55 } 56 } 57 } 58 vis[u] = false; 59 } 60 if(dis[2*n*m+2] == inf) return false; 61 return true; 62 } 63 void end(){ 64 int u, p, sum = inf; 65 for(u = 2*n*m+2; u !=2*n*m+1; u = edge[p^1].v){//0是超级源点 66 p = pre[u]; 67 sum = min(sum, edge[p].cap); 68 } 69 for(u = 2*n*m+2; u != 2*n*m+1; u = edge[p^1].v){ 70 p = pre[u]; 71 edge[p].cap -= sum; 72 edge[p^1].cap += sum; 73 ans += sum * edge[p].cost; // cost记录的为单位流量费用,必须得乘以流量。 74 } 75 flow+=sum; 76 } 77 78 void input(){ 79 memset(mp,0,sizeof(mp)); 80 for(int i=1;i<=n;i++){ 81 for(int j=1;j<=m;j++) 82 cin>>mp[i][j]; 83 } 84 } 85 void build(){ 86 for(int i=1;i<=n;i++) 87 for(int j=1;j<=m;j++){ 88 for(int k=j+1;k<=m;k++){ 89 if(mp[i][j]==mp[i][k]) 90 addEdge((i-1)*m+j,(i-1)*m+k+n*m,1,k-j-1-(mp[i][j]-'0')); 91 else 92 addEdge((i-1)*m+j,(i-1)*m+k+n*m,1,k-j-1); 93 94 } 95 for(int k=i+1;k<=n;k++){ 96 if(mp[i][j]==mp[k][j]) 97 addEdge((i-1)*m+j,(k-1)*m+j+n*m,1,k-i-1-(mp[i][j]-'0')); 98 else 99 addEdge((i-1)*m+j,(k-1)*m+j+n*m,1,k-i-1); 100 } 101 102 } 103 for(int i=1;i<=n*m;i++){ 104 addEdge(2*m*n+1,i,1,0); 105 addEdge(0,i+n*m,1,0); 106 addEdge(i+n*m,2*n*m+2,1,0); 107 } 108 addEdge(2*n*m+1,0,k,0); 109 } 110 int cas; 111 int main(){ 112 int tt=1; 113 scanf("%d",&cas); 114 while(cas--){ 115 scanf("%d%d%d",&n,&m,&k); 116 input(); 117 init(); 118 build(); 119 while(spfa())end(); 120 if(flow==n*m){ 121 printf("Case %d : %d ",tt++,-ans); 122 } 123 else{ 124 printf("Case %d : -1 ",tt++); 125 } 126 } 127 }