zoukankan      html  css  js  c++  java
  • uva 10558 A Brief Gerrymander

    很多次OJ都有 这题,其中poj 1933 , 题目的名字都是一样的

    题意:一个棋盘,横竖线都是从1到100标号(竖线从左到右标,横线从下到上标),输入n表示有n个被标记的格子,是给出这个格子的左下角坐标,然后输入m,在输入m个数,表示在这些竖线的地方切开棋盘(其实只切了m-2刀,因为2刀必须是1和100,相当于没有),然后输入A,表示你要在横上上切A刀(其实也只是A-2刀,因为2刀必须在横线的1和100)。那么就可以把棋盘很多个大小不一的方块(矩形),只要这些方块中有被标记的小格子(1个或多个),那么这个方块就是一个选区,我们是要使到选区的个数最多

    要先预处理,我们先来说明几个数组做意义。首先题目已经切好了竖线,一共m条,那么整个正方形的棋盘就被分成了m-1个长条状的矩形。

    数组only[i][j]的意思是在第j部分(原棋盘已经被分为了m-1个部分)第i条横线上的方块有没有被标记的方块,有为1,无为0,所以only数组可以设为bool型

    数组f[i][j]的意思是在第j部分(原棋盘已经被分为了m-1个部分),从最底下的横线开始在第i条横线上的方块一共有多少行是有标记的方块的,是一个累加数组

    也就是程序里面这个语句

     

    only[i][k]=f[i][k]=0;
    for(j=a[k]; j<a[k+1]; j++)
    if(g[i][j]==1)
    { only[i][k]=1; break; }
    f[i][k]=only[i][k];
    f[i][k]+=f[i-1][k];

     

    先算出当前这行的only[i][k]然后就赋值给f[i][k],然后再累加f[i-1][k]

    然后就是s[i][j]表示第i横线和第j横线之间有多少个选区,这也是和m-1个部分有关的,s[i][j]的值的范围一定在[0,m-1],即一个部分要么是选区要么不是,与它有多少行并没有多大关系

    dp[i][k],表示在横线区间(i,100)内选k条横线进行切割的最优解(注意第i条和第100条是不能选的,所以每一个状态能选的横线的条数就是 100-1-i).

    状态转移方程就是dp[i][k]=s[i][j]+d[j][k-1]   什么意思呢

    假设现在是从第i条横线开始,然后找k条切线,哪k条我们不知道,所以我们可以枚举(i,100)里面的直线,假设我们找到的最下面的横线是k,那么也就是说(i,k)之间是没有任何横线的,所以我们就用上了s[i][k],切了第一条之后,相当于分解出了子问题,下一步我们就是以k作为底边,然后找到最靠近k的横线k'',那么可知(k,k'')之间也是不会有其他切线的

    我们在选“第一条”切线也就是最靠近底边的那条很切线时有一个人原则,就是100-i-1 > k-1  ,即假设选了这条横线那么剩下的横线至少得有k-1条,因为还有j-1条线可以去切

     

    另外,当k=0时,即全部的切线都选完了,已经切完了,那么dp[i][0]=s[i][100],即剩下来的那部分不用切了,为一整块,那么它的选区个数就是s[i][100]

     

    这个解释可能语言表达上有些问题,如果看不明白多看几次应该可以了,而且这题的英语比较纠结,题意搞了很久都不懂。另外这道题的DP过程还是比较容易理解的,就是预处理部分比较多细节,注意一些等于号的问题,要过这题不难的

    其中DP采用了记忆化搜索实现,时间是很快乐,我交了很多次,每次都进了TOP20

     

    #include <stdio.h>
    #include <string.h>
    #define MAX 110
    bool g[MAX][MAX],only[MAX][MAX],visit[MAX][MAX];
    int f[MAX][MAX],s[MAX][MAX];
    int a[MAX];
    int dp[MAX][MAX],path[MAX][MAX];
    int m,A;
    
    void init()
    {
        int i,j,k;
    
        for(k=1; k<m; k++)
            for(i=1; i<100; i++)
            {
                only[i][k]=f[i][k]=0;
                for(j=a[k]; j<a[k+1]; j++)
                    if(g[i][j]==1)
                    { only[i][k]=1; break; }
                f[i][k]=only[i][k];
                f[i][k]+=f[i-1][k];
            }
    
            for(i=1; i<100; i++)
                for(j=i+1; j<=100; j++)
                {
                    s[i][j]=0;
                    for(k=1; k<m; k++)
                        if(f[j-1][k]-f[i][k]+only[i][k])
                            s[i][j]++;
                }
    
            return ;
    }
    
    int DP(int i , int j)
    {
        int k,ans;
        if(visit[i][j])
            return dp[i][j];
    
        visit[i][j]=1;
        if(j==0)
            return dp[i][j]=s[i][100];
    
        dp[i][j]=0;
        for(k=i+1; k<100; k++)
        {
            if(100-k-1 < j-1)
                break;
            ans=DP(k,j-1);
            if(ans+s[i][k] > dp[i][j])
            {
                dp[i][j]=ans+s[i][k];
                path[i][j]=k;
            }
        }
    
        return dp[i][j];
    }
    
    void print_path(int i , int j)
    {
        int k;
        if(j<=0)  return ;
        k=path[i][j];
        printf(" %d",k);
        print_path(k,j-1);
        return ;
    }
    
    int main()
    {
        int i,n,j,k;
        while(1)
        {
            scanf("%d",&n);
            if(n==-1) break;
    
            memset(g,0,sizeof(g));
            for(i=1; i<=n; i++) 
            {
                scanf("%d%d",&j,&k);
                g[k][j]=1;
            }
    
            scanf("%d",&m);
            for(i=1; i<=m; i++)
            scanf("%d",&a[i]);
    
            scanf("%d",&A);
    
            init();
    
            memset(visit,0,sizeof(visit));
            DP(1,A-2);
        //    printf("max=%d\n",dp[1][A-2]);
            printf("%d",A);
            printf(" 1");
            print_path(1,A-2);
            printf(" 100\n");
        }
        return 0;
    }
  • 相关阅读:
    【2017-4-26】Winform 公共控件 菜单和工具栏
    【2017-4-24】Winform 简单了解 窗口属性
    【2017-4-21】ADO.NET 数据库操作类
    【2017-4-21】ADO.NET 防止字符串注入攻击
    【2017-4-19】ADO.NET 数据库链接,基础增删改
    Vue#条件渲染
    Vue#Class 与 Style 绑定
    Vue#计算属性
    Vue入门笔记#数据绑定语法
    Vue入门笔记#过渡
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2711436.html
Copyright © 2011-2022 走看看