NOIP200003方格取数 |
难度级别: D; 编程语言:不限;运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
XYZ 是首师大附中信息技术团编程大神之一,尤其近两个月水平提升迅猛,一发不可收拾。老师说他前途不可估量,于是他有一点小骄傲,好像没有什么题能难住他。这让另一位编程高手 WJH 看不下去了,于是要求出一道题考考他,如果 10 分钟内做不出来,以后不许再这么“嚣张”,XYZ 欣然同意。WJH 要求 XYZ 帮助 ZYT 解决一个问题: |
输入
|
第一行为一个整数 N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
|
输出
|
只需输出一个整数,表示2条路径上取得的最大的和。
|
输入示例
|
8
2 3 13 2 6 6 3 5 7 4 4 14 5 2 21 5 6 4 6 3 15 7 2 14 0 0 0 |
输出示例
|
67
|
其他说明
|
数据范围:所有正整数都不会超过1000,太大了杨老师没那么多积分给的!
|
第一种方法是,我们可以使用费用流,对于每个点我们拆成两个点i,i`,并从i向i`连两条弧,容量均为1,一条费用为0,一条费用为-wi.
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i!=-1;i=next[i])
using namespace std;
inline int read() {
int x=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
const int maxm=;
const int INF=;
struct ZKW {
int n,m,s,t,first[maxn],next[maxm];
int ans,cost;
int vis[maxn],inq[maxn],d[maxn];
struct Edge {int from,to,flow,cost;}edges[maxm];
void init(int n) {
this->n=n;m=;
memset(first,-,sizeof(first));
}
void AddEdge(int from,int to,int cap,int cost) {
edges[m]=(Edge){from,to,cap,cost};next[m]=first[from];first[from]=m++;
edges[m]=(Edge){to,from,,-cost};next[m]=first[to];first[to]=m++;
}
int BFS() {
queue<int> Q;
rep(i,,n) d[i]=INF;
d[t]=;inq[t]=;Q.push(t);
while(!Q.empty()) {
int x=Q.front();Q.pop();inq[x]=;
ren {
Edge& e=edges[i^];
if(e.flow&&d[e.from]>d[x]+e.cost) {
d[e.from]=d[x]+e.cost;
if(!inq[e.from]) inq[e.from]=,Q.push(e.from);
}
}
}
rep(i,,m-) edges[i].cost+=d[edges[i].to]-d[edges[i].from];
cost+=d[s];return d[s]!=INF;
}
int DFS(int x,int a) {
if(x==t||!a) {ans+=cost*a;return a;}
int flow=,f;vis[x]=;
ren {
Edge& e=edges[i];
if(e.flow&&!e.cost&&!vis[e.to]&&(f=DFS(e.to,min(a,e.flow)))) {
flow+=f;a-=f;
e.flow-=f;edges[i^].flow+=f;
if(!a) break;
}
}
return flow;
}
int solve(int s,int t) {
ans=cost=;this->s=s;this->t=t;
while(BFS()) do memset(vis,,sizeof(vis));while(DFS(s,INF));
return ans;
}
}sol;
int n,w[][];
int id(int x,int y,int t) {return t*n*n+(x-)*n+y;}
int main() {
n=read();sol.init(n*n*);
while() {
int x=read(),y=read(),v=read();
if(!x) break;
w[x][y]=v;
}
rep(i,,n) rep(j,,n) {
sol.AddEdge(id(i,j,),id(i,j,),,-w[i][j]);
sol.AddEdge(id(i,j,),id(i,j,),,);
if(i+<=n) sol.AddEdge(id(i,j,),id(i+,j,),,);
if(j+<=n) sol.AddEdge(id(i,j,),id(i,j+,),,);
}
printf("%d\n",-sol.solve(id(,,),id(n,n,)));
return ;
}
第二种方法是,我们使用DP。考虑一个人走两遍相当于两个人同时走,设f[x1][y1][x2][y2]表示第一个人走到了(x1,y1),第二个人走到了(x2,y2)最大收益。
转移时枚举上一次两个人在哪里,并加上这一步造成的收益:f[x1][y1][x2][y2]=Max(f[x1-1][y1][x2-1][y2],f[x1][y1-1][x2][y2-1],f[x1-1][y1][x2][y2-1],f[x1][y1-1][x2-1][y2])+w[x1][y1]+(x1!=x2||y1!=y2)*w[x2][y2].时间复杂度为O(N^4)
注意因为同时走,x1+y1恒等于x2+y2,可以将时间复杂度优化为O(N^3).
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i!=-1;i=next[i])
using namespace std;
inline int read() {
int x=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
int n,w[maxn][maxn],f[maxn][maxn][maxn];
int max(int a,int b,int c,int d) {
return max(max(a,b),max(c,d));
}
int dp(int x1,int y1,int x2) {
if(x1==&&y1==) return w[][];
int y2=x1+y1-x2;
if(x1<||x2<||x1>n||x2>n||y1<||y2<||y1>n||y2>n) return -<<;
int& ans=f[x1][y1][x2];
if(ans>=) return ans;
int tmp=max(dp(x1-,y1,x2-),dp(x1,y1-,x2-),dp(x1,y1-,x2),dp(x1-,y1,x2));
return ans=tmp+w[x1][y1]+(x1==x2?:)*w[x2][y2];
}
int main() {
memset(f,-,sizeof(f));
n=read();
while() {
int x=read(),y=read(),v=read();
if(!x) break;
w[x][y]=v;
}
printf("%d\n",dp(n,n,n));
return ;
}