NOI 模拟试题(三)

                       阵列弱点

【题目背景】
最后的会战:水滴对阵 n*m 架战舰。
悲剧的阵列喂饱了水滴,上百架战舰就是一串挂炮。
【题目描述】
抽象会战前的阵列为一个n*m的矩阵。每一个元素对应一个战舰。每一个战舰有一个
特征值。水滴希望找到一条特征值先下降后上升(可以只变大或者只变小)的路径(即一
个元素序列,满足相邻两个元素在阵列中的曼哈顿距离为1,或者说相邻两个元素上下左右
四连通,且每一个元素在路径中最多出现一次)。为了取得最好的爆炸效果,希望你能求
出最长的路径及其方案。
【输入】(weakness.in)
第一行两个数 n,m 意义同上
接下来 n 行,每行 m 个数,用空格分开,描述了整个阵列的特征值。
【输出】(weakness.out)
第一行只有一个数 s,表示最长满足要求的路径的元素个数
接下来 s 行,每一行两个数(用空格分开)x y。第 i+1 行表示第 i 个元素的坐标(行列
表示)
【样例输入】
4 4
5 14 3 4
1 6 9 8
10 15 2 12
13 11 7 16
【样例输出】
6
4 4
3 4
2 4
1 4
1 3
2 3
【数据规模】
n,m<=60
阵列中的任意两个元素不相等,且在 32 位长整型范围内

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=20010,M=2000010,K=62,INF=1000000000;
int cnt,fir[N],to[M],nxt[M],cap[M],val[M];
void addedge(int a,int b,int c,int v){
    nxt[++cnt]=fir[a];to[fir[a]=cnt]=b;cap[cnt]=c;val[cnt]=v;
    nxt[++cnt]=fir[b];to[fir[b]=cnt]=a;cap[cnt]=0;val[cnt]=-v;
}
int q[M],front,back;
bool vis[N];
int dis[N],path[N];
int BFS(int S,int T){
    q[front=back]=S;vis[S]=true;
    for(int i=1;i<=T;i++)dis[i]=-INF;
    while(front<=back){
        int x=q[front++];vis[x]=false;
        for(int i=fir[x];i;i=nxt[i])
            if(cap[i]&&dis[to[i]]<dis[x]+val[i]){
                dis[to[i]]=dis[x]+val[i];
                if(!vis[to[i]])q[++back]=to[i];
                vis[to[i]]=true;path[to[i]]=i;
            }
    }
    return dis[T];
}   

void Aug(int S,int T){
    int p=T;
    while(p!=S){
        cap[path[p]]-=1;
        cap[path[p]^1]+=1;
        p=to[path[p]^1];
    }
}

int McMf(int S,int T){
    int ret=0,d;
    while((d=BFS(S,T))!=-INF){
        ret+=d;Aug(S,T);
    }
    return ret;
}

int a[K][K],st[N],top;
int n,m,ans,ansx,ansy,idx,S,T;
int id[K][K],id2[K][K],w[K][K];
void Init(){
    memset(fir,0,sizeof(fir));
    S=0;T=2*n*m+1;cnt=1;
}
int Solve(int x,int y){
    Init();
    addedge(S,id2[x][y],2,0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            addedge(id[i][j],id2[i][j],1,0);
            addedge(id2[i][j],T,1,0);
            if(id[i+1][j]&&a[i][j]<a[i+1][j])addedge(id2[i][j],id[i+1][j],1,1);
            if(id[i-1][j]&&a[i][j]<a[i-1][j])addedge(id2[i][j],id[i-1][j],1,1);
            if(id[i][j+1]&&a[i][j]<a[i][j+1])addedge(id2[i][j],id[i][j+1],1,1);
            if(id[i][j-1]&&a[i][j]<a[i][j-1])addedge(id2[i][j],id[i][j-1],1,1);
        }
    return McMf(S,T)+1; 
}
void DFS(int x){
    st[++top]=x;
    for(int i=fir[x+n*m];i;i=nxt[i])
        if(to[i]!=T&&cap[i]==0)DFS(to[i]);
}
int main(){
    freopen("weakness.in","r",stdin);
    freopen("weakness.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            id[i][j]=++idx;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            id2[i][j]=++idx;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);

    for(int x=1;x<=n;x++)
        for(int y=1;y<=m;y++){
            int tmp=Solve(x,y);
            if(tmp>ans){
                ans=tmp;
                ansx=x;
                ansy=y;
            }
        }
    Solve(ansx,ansy);
    printf("%d\n",ans);
    int flag=0,pos=id2[ansx][ansy];
    for(int i=fir[pos];i;i=nxt[i])
        if(cap[i]==0){
            if(to[i]==id[ansx][ansy])continue;
            if(to[i]==S||to[i]==T)continue;
            top=0;DFS(to[i]);
            if(flag){
                printf("%d %d\n",ansx,ansy);
                for(int j=1;j<=top;j++){
                    int x=st[j];
                    printf("%d %d\n",(x-1)/m+1,(x-1)%m+1);
                }
                break;
            }
            else{
                flag=1;
                while(top){
                    int x=st[top--];
                    printf("%d %d\n",(x-1)/m+1,(x-1)%m+1);
                }
            }
        }   
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值