NOIP200003方格取数
难度级别: D; 编程语言:不限;运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B
试题描述

XYZ 是首师大附中信息技术团编程大神之一,尤其近两个月水平提升迅猛,一发不可收拾。老师说他前途不可估量,于是他有一点小骄傲,好像没有什么题能难住他。这让另一位编程高手 WJH 看不下去了,于是要求出一道题考考他,如果 10 分钟内做不出来,以后不许再这么“嚣张”,XYZ 欣然同意。WJH 要求 XYZ 帮助 ZYT 解决一个问题:
    现有 N*N 的方格图( N<=10,将其中的某些方格中填入正整数,而其它的方格中放入数字 0。如下图所示(见样例): 
               
    ZYT 从图的左上角的 A 点出发,可以向下行走,也可以向右走,直到到达右下角的 B 点。在走过的路上,ZYT 可以取走方格中的数(取走后的方格中将变为数字 0 )。从 A 点到 B 点共走两次,你必须指导ZYT所取数之和最大。如果得到正确的最大值他可以找杨老师获得该最大数的 10 倍积分,如果最大值错误,要跑步的( 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 ;
}

NOIP200003方格取数的更多相关文章

  1. HDU 1565&1569 方格取数系列(状压DP或者最大流)

    方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  2. vijos 1563 疯狂的方格取数

    P1653疯狂的方格取数 Accepted 标签:天才的talent[显示标签]   背景 Due to the talent of talent123,当talent123做完NOIP考了两次的二取 ...

  3. [HDU 1565+1569] 方格取数

    HDU 1565 方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  4. 网络流(最大流) HDU 1565 方格取数(1) HDU 1569 方格取数(2)

      HDU 1565 方格取数(1) 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的 ...

  5. HDU-1565 方格取数(1)

    http://acm.hdu.edu.cn/showproblem.php?pid=1565 方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Me ...

  6. BZOJ 1475: 方格取数( 网络流 )

    本来想写道水题....结果调了这么久!就是一个 define 里面少加了个括号 ! 二分图最大点权独立集...黑白染色一下 , 然后建图 : S -> black_node , white_no ...

  7. [动态规划]P1004 方格取数

    ---恢复内容开始--- 题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0.如下图所示(见样例): A 0 0 0 0 0 0 0 0 0 ...

  8. P2045 方格取数加强版

    P2045 方格取数加强版 题目描述 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格 ...

  9. 线性规划与网络流24题●09方格取数问题&13星际转移问题

    ●(做codevs1908时,发现测试数据也涵盖了1907,想要一并做了,但因为“技术”不佳,搞了一上午) ●09方格取数问题(codevs1907  方格取数3) 想了半天,也没成功建好图: 无奈下 ...

随机推荐

  1. intellij idea 如何更改编辑器文本字体和大小

    换上了intellij idea之后,第一件事就是想要改变下文字字体,因为在我这个27寸的2k分辨率的屏幕上,文字显然太小了. intellij idea字体设值分成两部分,一部分是UI部分字体字号设 ...

  2. Android读写assets、raw、sdard和工程文件的方法

    Android开发离不开对文件的操作,前面的文章“Android简易数据存储之SharedPreferences”和“Android数据存储之SQLite的操作”,分别讲解了简单的数据的存储和数据库数 ...

  3. Cocos2d-x 学习资料推荐

    最近在看Cocos2d-x ,官网的资料太少了,下面推荐一些比较好的教程,不断更新中. 1. cocos2d-x高级开发教程 如果你懂得objective-c 那么一定要看看这本书,这里面有许多C++ ...

  4. Java中必须了解的常用类

    1.Java的包装类 基本数据类型我们都很熟悉,例如:int.float.double.boolean.char等,基本数据类型不具备对象的特征,不能调用方法,一般能实现的功能比较简单,为了让基本数据 ...

  5. canva实践小实例 —— 马赛克效果

    前面给大家带来了操作像素的API,此时此刻,我觉得应该配以小实例来进行进一步的说明和演示,以便给大家带来更宽广的视野和灵感,你们看了我的那么多的文章,应该是懂我的风格,废话不多说,进入正题: 这次给大 ...

  6. Android学习笔记——文件路径(/mnt/sdcard/...)、Uri(content://media/external/...)学习

    一.URI 通用资源标志符(Universal Resource Identifier, 简称"URI"). Uri代表要操作的数据,Android上可用的每种资源 - 图像.视频 ...

  7. sqlserver临时启用和关闭约束

    select  'ALTER TABLE ['  + b.name +  '] NOCHECK CONSTRAINT ' +  a.name +';' as  禁用约束   from  sysobje ...

  8. SQL中行列转换Pivot

    --建表 ),课程 ),分数 int) --插入数据 ) ) ) ) ) ) 1.静态行转列(确定有哪些列) select 姓名, end)语文, end)数学, end)物理 from tb gro ...

  9. Oracle Undo与脏读解析

    Undo就是用来记录保存事务操作过程中的数据,如果事务发生错误,可以之前的数据进行填补. Undo segment 是保存在表空间上的.Undo 大小是固定的,既然是固定的也就是有限的.如果保存的记录 ...

  10. Xamarin.Android开发实践(十)

    Xamarin.Android之SQLiteOpenHelper 一.前言 在手机中进行网络连接不仅是耗时也是耗电的,而耗电却是致命的.所以我们就需要数 据库帮助我们存储离线数据,以便在用户未使用网络 ...