[NOIP2008]传纸条
先用双线动规写一遍,下午再来写费用流解法= =
解法1:双线动规
首先我们不难看出,由于纸条回来时不能与去时的路径重叠,“一来一回”和“从左上角分两条路走向右下角”这两个模型是等价的。于是我们可以把两条路到达的端点同时作为状态保存下来(dp[x1][y1][x2][y2])。又因为矩阵图的特殊性,左上角到右下角的所有路径长度均为两点的曼哈顿距离,我们可以让两点”同时“移动,即任何时刻两点走过的路程相同。这样,我们可以记当前状态为dp[i, j, k],其中 i 表示当前两点走到的横纵坐标之和,j表示第一条路径走到的横坐标,k表示第二条路径走到的横坐标。考虑到两条路径在途中不能重叠,我们约定j > k。其中每个位置最多都可以由两个点达到,那么每种状态最多要考虑2*2=4种前驱。这里要考虑一种特殊情况:当k == j-1时,两点都可以由(k, i-k-1)这一点走到,然而题目中规定路径中不能有重叠,那么这时我们应当排除”两点从同一点转移得到“的情况╮(╯▽╰)╭
这样,这道题就完美解决了→_→时间复杂度为O((M+N)MN)
p.s.截至发表前,这个解法在河南省实验中学COGS上排到了速度rank1→_→ (5ms)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 #include <algorithm>
3 #include <iostream>
4 #include <cctype>
5 #include <cmath>
6 #define maxn (52)
7 using namespace std;
8 #ifdef DEBUG
9 FILE *in = fopen("test","r");
10 #define out stdout
11 #else
12 FILE *in = fopen("message.in","r");
13 FILE *out = fopen("message.out","w");
14 #endif
15
16 inline void getint(int &k){
17 char c = fgetc(in);
18 while(!isdigit(c))c = fgetc(in);
19 k = c - '0';
20 while(isdigit(c = fgetc(in)))
21 k = k * 10 + (c - '0');
22 }
23 int m, n;
24 int Mat[maxn][maxn];//坐标从1开始
25 int dp[maxn<<1][maxn][maxn] = {0};
26 int main(){
27 int i, j, k, Max, t;
28 getint(m),getint(n);
29 for(i = 1;i <= m;++i)
30 for(j = 1;j <= n;++j)getint(Mat[i][j]);
31 dp[3][2][1] = Mat[2][1] + Mat[1][2];
32 for(i = 4;i < m+n;++i)
33 for(j = 2;j <= m;++j){
34 if(j == i)break;
35 for(k = max(1,i-n);k < j;++k){
36 Max = dp[i-1][j][k];
37 if((t=dp[i-1][j][k-1]) > Max)Max = t;
38 if((t=dp[i-1][j-1][k-1]) > Max)Max = t;
39 if(k!=j-1 && (t=dp[i-1][j-1][k])>Max)Max = t;
40 dp[i][j][k] = Max + Mat[j][i-j] + Mat[k][i-k];
41 }
42 }
43 i = m + n - 1;
44 fprintf(out,"%d\n",dp[i][m][m-1]);
45 return 0;
46 }
解法2:最大费用最大流
好吧现在已经是第二天了……感觉费用流写起来很费时间啊QAQ……
这道题的建模思路是这样的……将每个学生抽象成一条有向边,纸条抽象为流量,好心值抽象为费用,考虑以下两点限制条件:1.最多为2的流量流经(1,1)和(m,n)两学生;2.中间每个学生对应的容量为1(每个学生只会传一次纸条);3.在相邻的两个学生中,从偏左、偏上的学生对应边的终点向另一学生对应边的起点连边,容量为1,费用为0;4.源点为(1,1)学生的起点,汇点为(m,n)学生的终点。
对这样一个有向网络建图,求出最大费用最大流,即为原问题的解。由于问题的特殊性,spfa运行次数接近常数,因此这一算法的时间复杂度接近最短路。(然而,对于这样小的网格,费用流的解法其实有些浪费……)STL容器的时间常数似乎有点大,这个解法在COGS上花了9毫秒(而且是打开了-O2优化……醉了醉了)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 #include <algorithm>
3 #include <iostream>
4 #include <cctype>
5 #include <cmath>
6 #include <vector>
7 #include <queue>
8 #include <deque>
9 #include <ctime>
10 #define Vect vector<edge*>
11 #define pb push_back
12 #define iter(v) v::iterator
13 #define bg begin()
14 #define maxn (52)
15 #define maxv (5002)
16 using namespace std;
17
18 #if defined DEBUG
19 FILE *in = fopen("test","r");
20 #define out stdout
21 #else
22 FILE *in = fopen("message.in","r");
23 FILE *out = fopen("message.out","w");
24 #endif
25
26 inline void getint(int &k){
27 char c = fgetc(in);
28 while(!isdigit(c))c = fgetc(in);
29 k = c - '0';
30 while(isdigit(c = fgetc(in)))
31 k = k * 10 + (c - '0');
32 }
33
34 struct edge{
35 int w, vol, to;
36 edge(){}
37 edge(int W, int V, int T):w(W),vol(V),to(T){}
38 }E[maxv*6];
39 int preE[maxv];
40 int Ecnt = 0;
41 Vect adj[maxv];
42 int m, n, Vnum = 2, preV[maxv], ans = 0;
43 inline void addE(int &Ecnt, int f, int t, int w, int v){
44 E[Ecnt] = edge(w,v,t);
45 adj[f].pb(E + Ecnt++);
46 E[Ecnt] = edge(-w,0,f);
47 adj[t].pb(E + Ecnt++);
48 }
49
50 inline void init(){
51 int i, j, w;
52 getint(w);
53 addE(Ecnt, 0, 1, w, 2);
54 for(i = 1;i < n; ++i){
55 getint(w);
56 addE(Ecnt, Vnum-1, Vnum, 0, 1);
57 addE(Ecnt, Vnum, Vnum+1, w, 1);
58 Vnum += 2;
59 }
60 for(i = 1;i < m-1; ++i){
61 getint(w);
62 addE(Ecnt, Vnum-(n<<1)+1, Vnum, 0, 1);
63 addE(Ecnt, Vnum, Vnum+1, w, 1);
64 Vnum += 2;
65 for(j = 1;j < n; ++j){
66 getint(w);
67 addE(Ecnt, Vnum-1, Vnum, 0, 1);
68 addE(Ecnt, Vnum-(n<<1)+1, Vnum, 0, 1);
69 addE(Ecnt, Vnum, Vnum+1, w, 1);
70 Vnum += 2;
71 }
72 }
73 getint(w);
74 addE(Ecnt, Vnum-(n<<1)+1, Vnum, 0, 1);
75 addE(Ecnt, Vnum, Vnum+1, w, 1);
76 Vnum += 2;
77 for(j = 1;j < n; ++j){
78 getint(w);
79 addE(Ecnt, Vnum-1, Vnum, 0, 1);
80 addE(Ecnt, Vnum-(n<<1)+1, Vnum, 0, 1);
81 if(j < n-1)addE(Ecnt, Vnum, Vnum+1, w, 1);
82 else addE(Ecnt, Vnum, Vnum+1, 0, 2);
83 Vnum += 2;
84 }
85 }
86 bool spfa(int &d){
87 int dis[maxv] = {0}, tmp, t;
88 bool inq[maxv] = {0}, known[maxv] = {1};
89 queue<int> Q;
90 iter(Vect) it;
91 inq[0] = 1, Q.push(0);
92 while(!Q.empty()){
93 tmp = Q.front(), Q.pop(), inq[tmp] = 0;
94 it = adj[tmp].begin();
95 for(;it != adj[tmp].end();++it){
96 if((*it)->vol <= 0)continue;
97 t = dis[tmp] + (*it)->w;
98 if(t < 0)continue;
99 if(!known[(*it)->to] || t > dis[(*it)->to]){
100 dis[(*it)->to] = t;
101 known[(*it)->to] = 1;
102 preE[(*it)->to] = *it - E;
103 preV[(*it)->to] = tmp;
104 if(!inq[(*it)->to])inq[(*it)->to] = 1, Q.push((*it)->to);
105 }
106 }
107 }
108 d = dis[Vnum-1];
109 if(known[Vnum-1])return 1;
110 else return 0;
111 }
112 inline void aug(int &dis){
113 int k = Vnum - 1;
114 ans += dis;
115 while(k != 0){
116 E[preE[k]].vol -= 1;
117 E[preE[k]^1].vol += 1;
118 k = preV[k];
119 }
120 dis = 0;
121 }
122 int main(){
123 getint(m),getint(n);
124 init();
125 int dis = 0;
126 while(spfa(dis))
127 aug(dis);
128 fprintf(out,"%d\n",ans);
129 #if defined DEBUG
130 cout << (double)clock() / CLOCKS_PER_SEC;
131 #endif
132 return 0;
133 }