很套路的一道题,做过无限之环来看这道题还是十分好想的
因为我比较菜,考试的时候就没能想出来。
按照套路,把格子黑白染色,然后考虑图的特点,到一个地方一定拐弯。
并且,对于一条回路来说,正向和反向的贡献都是一样的。
接下来,我们指定黑、白格子的流向,考虑一个白格子的流出一定是某个黑格子的流入。那么就好办了,我们规定黑格子横进竖出,白格子横出竖进。这样我们把问题抽象成一张图,对于每一个格子可以选择从进向自己连边,自己向出连边,相当于每种进的方案都可以选择两种出去的方案。但是题目还有一个隐含条件,一个点只能经过一条流量,也就是出入度都是1,想到限制出入度便想到了最小路劲覆盖的模型。
于是,初步的构图便完成了。
1、套用最小路径覆盖模型把每一个格子拆两个点u1, u2。S->u1->u2->T费用都是0,流量都是1,考虑到一个格子不经过的情况,相当于一个自环,所以u1->u2有连边。
2、格子X的连边分为:1、黑色,左右两边是A, B,A1->X2, B1->X2,上下两边是C, D,X1->C2,X1->D2。2、白色,把上面的连边反过来X1->A2, X1->B2, C1->X2, D1->X2
最后,对于必须走的格子,也就是不能有自环,我们去掉中间u1->u2的边即可。
辣鸡题目卡我常数……
把TLE的点删了下面的程序就能过了
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int maxm = 5e3 + 5;
const int maxn = 2e3 + 5;
const int N = 35;
const int inf = 0x3f3f3f3f;
inline char gc(){
static char buf[5000000], *p1 = buf, *p2 = buf;
return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, 5000000, stdin), p1 == p2) ? EOF : *p1++;
}
int f = 1;
inline void read(int & x) {
x = 0, f = 1; static char c = gc();
while(!isdigit(c)) {
if(c == '-') f = -1;
c = gc();
}
while(isdigit(c)) x = x * 10 + c - '0', c = gc();
x *= f;
}
struct edge {
int v, w, cost, next;
} e[maxm << 2];
int head[maxn], cnt;
void adde(const int &u, const int &v, const int &w, const int &cost) {
//cerr << u << "---" << w << "/" << cost << "-->" << v << endl;
e[cnt] = (edge) {v, w, cost, head[u]};
head[u] = cnt++;
e[cnt] = (edge) {u, 0, -cost, head[v]};
head[v] = cnt++;
}
int q[maxn * maxn], frt, bk;
int vis[maxn], fa[maxn], fw[maxn], id[maxn], dis[maxn];
LL dw;
const int Lf = 0, Rt = 1;
int C[N][N], R[N][N], ban[N][N];
int n, m, K, u, v, ND[N][N][2], dcnt, S, T;
int SPFA(int s, int t) {
frt = bk = 1;
q[bk++] = s, vis[s] = 1;
for(register int i = 1; i <= dcnt; ++i)
dis[i] = -inf;
dis[s] = 0;
while(frt < bk) {
int u = q[frt++];
vis[u] = 0;
for(register int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(!e[i].w) continue;
if(dis[v] < dis[u] + e[i].cost) {
dis[v] = dis[u] + e[i].cost;
fa[v] = u, fw[v] = e[i].w, id[v] = i;
if(vis[v]) continue;
q[bk++] = v;
vis[v] = 1;
}
}
}
return dis[t];
}
int Flow(int u, int t, int mn) {
if(u == t) return mn;
mn = Flow(fa[u], t, min(mn, fw[u]));
e[id[u]].w -= mn, e[id[u] ^ 1].w += mn;
return mn;
}
int MaxFlow(int s, int t) {
int ans = 0, f = 0, tmp;
while(SPFA(s, t) != -inf) {
tmp = Flow(t, s, inf), f += tmp;
ans += 1ll * dis[t] * tmp;
}
return f == dw ? ans : -1;
}
int spc[N * N][2];
int main() {
//freopen("bounce.in", "r", stdin);
//freopen("bounce.out", "w", stdout);
int t;
read(t);
while(t--) {
memset(head, -1, sizeof(head));
memset(ban, 0, sizeof(ban));
cnt = 0, dcnt = 0;
read(n), read(m), dw = n * m;
for(register int i = 1; i <= n; ++i)
for(register int j = 1; j < m; ++j)
read(C[i][j]);
for(register int i = 1; i < n; ++i)
for(register int j = 1; j <= m; ++j)
read(R[i][j]);
read(K);
for(register int i = 1; i <= K; ++i) {
read(u), read(v);
ban[u][v] = 1, spc[i][0] = u, spc[i][1] = v;
}
S = ++dcnt, T = ++dcnt;
for(register int i = 1; i <= n; ++i)
for(register int j = 1; j <= m; ++j) {
ND[i][j][Lf] = ++dcnt;
ND[i][j][Rt] = ++dcnt;
adde(S, ND[i][j][Lf], 1, 0);
if(!ban[i][j]) adde(ND[i][j][Lf], ND[i][j][Rt], 1, 0);
adde(ND[i][j][Rt], T, 1, 0);
}
for(register int i = 1; i <= n; ++i)
for(register int j = 1; j <= m; ++j) {
if((i + j) & 1) { //横进竖出
if(j < m) adde(ND[i][j + 1][Lf], ND[i][j][Rt], 1, C[i][j]);
if(j > 1) adde(ND[i][j - 1][Lf], ND[i][j][Rt], 1, C[i][j - 1]);
if(i < n) adde(ND[i][j][Lf], ND[i + 1][j][Rt], 1, R[i][j]);
if(i > 1) adde(ND[i][j][Lf], ND[i - 1][j][Rt], 1, R[i - 1][j]);
} else { //竖进横出
if(j < m) adde(ND[i][j][Lf], ND[i][j + 1][Rt], 1, C[i][j]);
if(j > 1) adde(ND[i][j][Lf], ND[i][j - 1][Rt], 1, C[i][j - 1]);
if(i < n) adde(ND[i + 1][j][Lf], ND[i][j][Rt], 1, R[i][j]);
if(i > 1) adde(ND[i - 1][j][Lf], ND[i][j][Rt], 1, R[i - 1][j]);
}
}
int ans = MaxFlow(S, T);
if(ans == -1) printf("Impossible\n");
else printf("%d\n", ans);
}
return 0;
}甚至,我贴了份ZKW也没有过,mmp
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int maxm = 1e4 + 5;
const int maxn = 5e3 + 5;
const int N = 40;
const int inf = 0x3f3f3f3f;
inline char gc(){
static char buf[5000000], *p1 = buf, *p2 = buf;
return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, 5000000, stdin), p1 == p2) ? EOF : *p1++;
}
int f = 1;
inline void read(int & x) {
x = 0, f = 1; static char c = gc();
while(!isdigit(c)) {
if(c == '-') f = -1;
c = gc();
}
while(isdigit(c)) x = x * 10 + c - '0', c = gc();
x *= f;
}
LL dw;
const int Lf = 0, Rt = 1;
int C[N][N], R[N][N], ban[N][N];
int n, m, K, u, v, ND[N][N][2], dcnt;
namespace ZKW {
bool vis[maxn];int dist[maxn];
//解释一下各数组的含义:vis两个用处:spfa里的访问标记,増广时候的访问标记,dist是每个点的距离标号
int s,t,ans=0;
//s是起点,t是终点,ans是费用答案
int nedge=-1,p[maxm << 2],c[maxm << 2],cc[maxm << 2],nex[maxm << 2],head[maxn];
//这里是边表,解释一下各数组的含义:p[i]表示以某一点出发的编号为i的边对应点,c表示编号为i的边的流量,cc表示编号为i的边的费用,nex和head不说了吧。。。
inline void addedge(int x,int y,int z,int zz){
p[++nedge]=y;c[nedge]=z;cc[nedge]=zz;nex[nedge]=head[x];head[x]=nedge;
}
inline void adde(int x, int y, int z, int zz){
addedge(x, y, z, zz);
addedge(y, x, 0, -zz);
}
//建边(数组模拟边表倒挂)
int q[maxn * maxn], frt, bk;
inline bool spfa(int s,int t){
memset(vis,0,sizeof vis),frt=bk=1;
for(int i=0;i<=dcnt;i++)dist[i]=1e9;dist[t]=0;vis[t]=1;
//首先SPFA我们维护距离标号的时候要倒着跑,这样可以维护出到终点的最短路径
q[bk++]=t;
//使用了SPFA的SLF优化(SLF可以自行百度或Google)
while(frt<bk){
int now=q[frt];++frt;
for(int k=head[now];k>-1;k=nex[k])if(c[k^1]&&dist[p[k]]>dist[now]-cc[k]){
//首先c[k^1]是为什么呢,因为我们要保证正流,但是SPFA是倒着跑的,所以说我们要求c[k]的对应反向边是正的,这样保证走的方向是正确的
dist[p[k]]=dist[now]-cc[k];
//因为已经是倒着的了,我们也可以很清楚明白地知道建边的时候反向边的边权是负的,所以减一下就对了(负负得正)
if(!vis[p[k]]){
vis[p[k]]=1;
q[bk++]=p[k];
}
}
vis[now]=0;
}
return dist[s]<1e9;
//判断起点终点是否连通
}
inline int dfs(int x,int low){
//这里就是进行増广了
if(x==t){vis[t]=1;return low;}
int used=0,a;vis[x]=1;
//这边是不是和dinic很像啊
for(int k=head[x];k>-1;k=nex[k])if(!vis[p[k]]&&c[k]&&dist[x]-cc[k]==dist[p[k]]){
//这个条件就表示这条边可以进行増广
a=dfs(p[k],min(c[k],low-used));
if(a)ans+=a*cc[k],c[k]-=a,c[k^1]+=a,used+=a;
//累加答案,加流等操作都在这了
if(used==low)break;
}
return used;
}
inline int costflow(){
int flow=0; ans=0;
while(spfa(s,t)){
//判断起点终点是否连通,不连通说明满流,做完了退出
vis[t]=1;
while(vis[t]){
memset(vis,0,sizeof vis);
flow+=dfs(s,1e9);
//一直増广直到走不到为止(这样也可以省时间哦)
}
}
return flow == dw ? -ans : -1;//这里返回的是最大流,费用的答案在ans里
}
}
int main() {
using namespace ZKW;
freopen("bounce.in", "r", stdin);
freopen("bounce.out", "w", stdout);
int T;
read(T);
while(T--) {
memset(nex,-1,sizeof(nex));
memset(head,-1,sizeof(head));
memset(ban, 0, sizeof(ban));
nedge = -1, dcnt = 0;
read(n), read(m), dw = n * m;
for(register int i = 1; i <= n; ++i)
for(register int j = 1; j < m; ++j)
read(C[i][j]);
for(register int i = 1; i < n; ++i)
for(register int j = 1; j <= m; ++j)
read(R[i][j]);
read(K);
for(register int i = 1; i <= K; ++i) {
read(u), read(v);
ban[u][v] = 1;
}
s = ++dcnt, t = ++dcnt;
for(register int i = 1; i <= n; ++i)
for(register int j = 1; j <= m; ++j) {
ND[i][j][Lf] = ++dcnt;
ND[i][j][Rt] = ++dcnt;
adde(s, ND[i][j][Lf], 1, 0);
if(!ban[i][j]) adde(ND[i][j][Lf], ND[i][j][Rt], 1, 0);
adde(ND[i][j][Rt], t, 1, 0);
}
for(register int i = 1; i <= n; ++i)
for(register int j = 1; j <= m; ++j) {
if((i + j) & 1) { //横进竖出
if(j < m) adde(ND[i][j + 1][Lf], ND[i][j][Rt], 1, -C[i][j]);
if(j > 1) adde(ND[i][j - 1][Lf], ND[i][j][Rt], 1, -C[i][j - 1]);
if(i < n) adde(ND[i][j][Lf], ND[i + 1][j][Rt], 1, -R[i][j]);
if(i > 1) adde(ND[i][j][Lf], ND[i - 1][j][Rt], 1, -R[i - 1][j]);
} else { //竖进横出
if(j < m) adde(ND[i][j][Lf], ND[i][j + 1][Rt], 1, -C[i][j]);
if(j > 1) adde(ND[i][j][Lf], ND[i][j - 1][Rt], 1, -C[i][j - 1]);
if(i < n) adde(ND[i + 1][j][Lf], ND[i][j][Rt], 1, -R[i][j]);
if(i > 1) adde(ND[i - 1][j][Lf], ND[i][j][Rt], 1, -R[i - 1][j]);
}
}
int ans = costflow();
if(ans == -1) printf("Impossible\n");
else printf("%d\n", ans);
}
return 0;
}
rockdu
没有帐号? 立即注册