链接:http://poj.org/problem?id=3422
卡卡
题意:卡卡的矩阵之旅,有一个n*n的矩阵,卡卡要从左上角走到右下角,每次他只能往右或往下走,卡卡可以走k遍这个矩阵,每个点有一个num值,卡卡走到这里可以获得num点,一个点只能获得一次num值,问卡卡走完k遍后身上num值最大可以是多少?
思路:其实看到这题时没思路,图论书上说了建图的方式,但没有说为什么,我不解,网上搜了一下解题报告,百度了两页,我看到的博客都是写了如何建图,但没有写为什么要这么建。。我觉得我真是弱渣,别人看了如何建图就立即能明白,我看了如何建图还是没明白,实在找不到讲解就自己想吧!自己想其实也不算难,理解了为什么拆点这道题其实比较简单,下面是我想完之后写的:
首先,贪心是肯定不行的,贪心的想法是每次取得尽量大的sum值,如果只走一遍这么做也需要考虑走过之后的情况,贪心勉强可以做,DP做更好。但是现在要走K遍,每一次走的路都会对之后的产生影响,因为每次只能向两个方向走,需要合理的规划这K次的走法,我DP很弱,不知道DP能不能做。
再按照网络流的想法来想:要走K遍,并且每个位置都有一个值num,把num来当做流量是不可取的,因为这个值是最终的答案,是要累加起来的,而流量则取路径中最大的那一个,不是累加。
那么把什么当做流量?可以把K值当做最终的最大流,超级源点、超级汇点的弧流量都是K,这就限定了只走K遍的情况,每次走的一整条路算作一个流量,总流量为K。
那num用来做什么?num就成了网络中的限制条件,当最大流相同时选尽量大的num值,其实就是最小费用最大流,只不过现在不是取最小费用,而是取最大费用。
现在再想矩阵中该如何建图,容易想到每一点向它右边和下边连一条弧,费用可以选用这个点的num值,但容量不好取。试想,如果容量取为INF,那卡卡可以每次都走总num值最大的那条路,走K遍,因为矩阵中没有对走的次数进行限制,只在超级源汇做了限制,但一个点的num值只能取一次,这么走等于一个点的num值取了K次。那容量应该取为1吗?也不对,取1限制了一个值只会取到一次,但问题随之而来:a点到b点如果有路,这条路也只能走一次,题意中没有这个规定,题意是说一个点可以走很多次,但是值只能取一次。容量取K和取INF是一样的,也不可取。
为了解决一个值只能取一次,但一个点可以走多次的问题,就有了网上广为流传的办法:拆点。
拆点了之后a点变为a‘和a‘‘,a‘→a‘‘表示取了a点的num值,这条弧的容量为1,费用为num,再连一条端点一样的弧,容量INF,费用为0,这样保证了一个点的值只取一次。取完后向a点可以走向的b点行进,b点也同样拆成了b‘和b‘‘,a‘‘到b‘连弧,容量为INF,表示一个点可以走很多次,这样就很好的解决了刚才的问题。
其实这种题应该多做几道就能想到拆点了。
建图:上一段差不多都说了,矩阵中的建图就如上面所说,没有费用的弧只要保证容量不比K小就行了。超级源点连向矩阵左上角,容量为k,费用0,矩阵最右下角的点a‘‘连向超级汇点,容量k,费用0,建图就是这样。
理解了之后搞了很久才AC,因为在SPFA里初始化dist数组范围小了,程序陷入了死循环。
还有一点,我输出的ans是全局变量,我在主函数里又定义了一个ans,程序优先使用局部变量,无限输出0。。我debug了超久才发现。。
#include<cstring> #include<string> #include<fstream> #include<iostream> #include<iomanip> #include<cstdio> #include<cctype> #include<algorithm> #include<queue> #include<map> #include<set> #include<vector> #include<stack> #include<ctime> #include<cstdlib> #include<functional> #include<cmath> using namespace std; #define PI acos(-1.0) #define MAXN 500100 #define eps 1e-7 #define INF 0x7FFFFFFF #define LLINF 0x7FFFFFFFFFFFFFFF #define seed 131 #define mod 1000000007 #define ll long long #define ull unsigned ll #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 struct node{ int u,v,w,cost,next; }edge[MAXN]; int head[5200],dist[5200],pree[5200],vis[5200]; int n,m,k,cnt,src,sink,ans,nn; int a[60][60]; void add_edge(int a,int b,int c,int d){ edge[cnt].v = b; edge[cnt].w = c; edge[cnt].cost = d; edge[cnt].next = head[a]; head[a] = cnt++; } void build_graph(){ int i,j; cnt = 0; memset(head,-1,sizeof(head)); src = 0; sink = n * n * 2 + 1; for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ int tt = (i-1)*n+j; add_edge(tt,tt+nn,1,-a[i][j]); add_edge(tt+nn,tt,0,a[i][j]); add_edge(tt,tt+nn,k,0); add_edge(tt+nn,tt,0,0); if(i+1<=n){ add_edge(tt+nn,tt+n,k,0); add_edge(tt+n,tt+nn,0,0); } if(j+1<=n){ add_edge(tt+nn,tt+1,k,0); add_edge(tt+1,tt+nn,0,0); } } } add_edge(src,1,k,0); add_edge(1,src,0,0); add_edge(nn*2,sink,k,0); add_edge(sink,nn*2,0,0); } bool spfa(){ int i,j; queue<int>q; q.push(src); memset(vis,0,sizeof(vis)); int lll = nn*2+5; for(i=0;i<=lll;i++) dist[i] = INF; dist[src] = 0; vis[src] = 1; while(!q.empty()){ int u = q.front(); q.pop(); vis[u] = 0; for(i=head[u];i!=-1;i=edge[i].next){ if(edge[i].w!=0&&dist[u]+edge[i].cost<dist[edge[i].v]){ dist[edge[i].v] = dist[u] + edge[i].cost; pree[edge[i].v] = i; if(!vis[edge[i].v]){ vis[edge[i].v] = 1; q.push(edge[i].v); } } } } if(dist[sink]!=INF) return true; return false; } int augment(){ int i,j; int delta = INF; for(i=sink;i!=src;i=edge[j^1].v){ j = pree[i]; delta = min(delta,edge[j].w); } for(i=sink;i!=src;i=edge[j^1].v){ j = pree[i]; edge[j].w -= delta; edge[j^1].w += delta; ans += edge[j].cost; } return delta; } int main(){ int i,j; int b,c,d; ans = 0; scanf("%d%d",&n,&k); nn = n * n; for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ scanf("%d",&a[i][j]); } } build_graph(); while(spfa()) augment(); printf("%d\n",-ans); return 0; }
POJ--3422--Kaka's Matrix Travels【最小费用最大流+拆点】,布布扣,bubuko.com
POJ--3422--Kaka's Matrix Travels【最小费用最大流+拆点】
原文地址:http://blog.csdn.net/zzzz40/article/details/38616147