Description
给出一个n行m列的矩阵,每个格子有一个花费TIJ,要求在每行选出恰好一个格子,使得这n个格子的Tij之和最小,每个格子还有一个权值Wij,对于相邻两行选择的格子(i,j1)和(i-1,j2),要求abs(j1-j2)<=W(i,j1)+W(i-1,j2)
对于20% 的数据,保证 T = 1 ;
对于另30% 的数据,保证 m < = 500 ;
对于100% 的数据,保证 T<= 5, 2≤n≤100 ,1 <=m<=5000.
0<= Tij 、Wij≤100000
Input
第一行个整数T,代表有T组数据 ,第二行两个整数 n,m;
对于每一组数据:
接下来 n行,每行 m个整数 Tij,再接下来 n行,每行 m个整数个整数 Wij。
题解
暴力的DP还是可以想得到的,f[i][j]表示当前选到第i行,选第j个的最小值,直接先枚举行,再枚举列,最后再枚举从上一行哪一列转移过来,这样就有50pts
#include<bits/stdc++.h>
using namespace std;
const int oo=10000005;
const int maxn=105;
const int maxm=5005;
int T,n,m;
int t[maxn][maxm];
int w[maxn][maxm];
int f[maxn][maxm];
template<class T>inline void read(T &x){
x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}
void nice(){
read(n);read(m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
read(t[i][j]),f[i][j]=oo;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
read(w[i][j]);
for(int i=1;i<=m;i++)
f[1][i]=t[1][i];
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=m;k++)
if(abs(j-k)<=w[i][j]+w[i-1][k])
f[i][j]=min(f[i][j],f[i-1][k]+t[i][j]);
int ans=oo;
for(int i=1;i<=m;i++) ans=min(ans,f[n][i]);
printf("%d
",ans);
}
int main(){
freopen("elect.in","r",stdin);
freopen("elect.out","w",stdout);
read(T);
while(T--) nice();
}
不过我只有20pts,因为题目只输入一次n,m.....
那么正解是什么?
abs在数轴上代表着两个数的距离,那么以j为圆心,Wij为半径的话就可以在数轴上划出一段区间,那么题目条件就变成了区间有重叠部分,只要j2所管辖区间有一点在j1区间,那么j1就可以从j2的答案转移,所以我们求j1的答案就变成了在区间内查找最小值,最后在求完这一行,在把每列的答案放进他所管辖区间。
这就是区间查询和区间修改,赤果果的线段树基本操作,可是一开始谁又想得到呢......
在弄完一行修改区间前,要先初始化把上一行的答案清掉。
#include<bits/stdc++.h>
using namespace std;
//abs(j1-j2):j1和j2的距离
//w(i,j1)可以看做一段区间
//只要w(i-1,j2)有一个点在这段区间内就满足abs(j1-j2)<=w(i,j1)+2(i-1,j2)
#define re register
const int oo=10000005;
const int maxn=105;
const int maxm=5005;
int T,n,m,num,root;
int a_l,a_r,a_v;
int t[maxn][maxm];
int lb[maxn][maxm],rb[maxn][maxm];
int f[maxn][maxm];
int mi[maxm<<1],ls[maxm<<1],rs[maxm<<1],tag[maxm<<1];
template<class T>inline void read(T &x){
x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}
__attribute__((optimize("-O2")))int min(int x,int y){return x<y ? x : y ;}
__attribute__((optimize("-O2")))int max(int x,int y){return x>y ? x : y ;}
__attribute__((optimize("-O2")))void build(int &rt,int l,int r){
if(!rt) rt=++num;
tag[rt]=mi[rt]=oo;
if(l==r) return ;
int mid=(l+r)>>1;
build(ls[rt],l,mid);
build(rs[rt],mid+1,r);
}
__attribute__((optimize("-O2")))inline void get_it(){
for(re int i=1;i<=num;i++) tag[i]=mi[i]=oo;
}
__attribute__((optimize("-O2")))inline void put_tag(int rt,int v){
mi[rt]=min(mi[rt],v);
tag[rt]=min(tag[rt],v);
}
__attribute__((optimize("-O2")))inline void push_down(int rt){
put_tag(ls[rt],tag[rt]);
put_tag(rs[rt],tag[rt]);
tag[rt]=oo;
}
__attribute__((optimize("-O2")))inline void update(int rt){
mi[rt]=min(mi[ls[rt]],mi[rs[rt]]);
}
__attribute__((optimize("-O2")))void modify(int rt,int l,int r){
if(a_l<=l&&r<=a_r){
put_tag(rt,a_v);
return ;
}
if(tag[rt]!=oo) push_down(rt);
int mid=(l+r)>>1;
if(a_l<=mid) modify(ls[rt],l,mid);
if(mid<a_r) modify(rs[rt],mid+1,r);
update(rt);
}
__attribute__((optimize("-O2")))int query(int rt,int l,int r){
if(a_l<=l&&r<=a_r) return mi[rt];
if(mi[rt]!=oo) push_down(rt);
int mid=(l+r)>>1;
int ans=oo;
if(a_l<=mid) ans=min(ans,query(ls[rt],l,mid));
if(mid<a_r) ans=min(ans,query(rs[rt],mid+1,r));
return ans;
}
__attribute__((optimize("-O2")))inline void init(){
for(int i=1;i<=m;++i){
f[1][i]=t[1][i];
a_l=lb[1][i];a_r=rb[1][i];a_v=f[1][i];
modify(1,1,m);
}
}
__attribute__((optimize("-O2")))inline void nice(){
for(re int i=1;i<=n;++i)
for(re int j=1;j<=m;++j)
read(t[i][j]);
for(re int i=1;i<=n;++i)
for(re int j=1;j<=m;++j){
int x;read(x);
lb[i][j]=max(1,j-x);
rb[i][j]=min(m,j+x);
}
get_it();
init();
for(re int i=2;i<=n;++i){
for(re int j=1;j<=m;++j){
a_l=lb[i][j];a_r=rb[i][j];
f[i][j]=query(1,1,m)+t[i][j];
}
get_it();
for(re int j=1;j<=m;++j){
a_l=lb[i][j];a_r=rb[i][j];a_v=f[i][j];
modify(1,1,m);
}
}
printf("%d
",mi[1]);
}
int main(){
freopen("elect.in","r",stdin);
freopen("elect.out","w",stdout);
read(T);read(n);read(m);
num=root=0;build(root,1,m);
while(T--) nice();
}
/*
1
3 5
9 5 3 8 7
8 2 6 8 9
1 9 7 8 6
0 1 0 1 2
1 0 2 1 1
0 2 1 0 2
*/
我写的线段树太垃圾了,评测姬跑不过,时限开大还是跑不过....就只好开O2[呸,不要脸]
听说将修改区间的L,R传入函数会快一些,那可能要怪yyr教的全局变量[甩锅]