UVA - 10558 A Brief Gerrymander 很抽象的题目

来源:互联网 发布:带着空间去民国淘宝 编辑:程序博客网 时间:2024/04/30 10:29

题目大意:把城市看成一个棋盘,点看成棋子。那么题意就比较好理解了。有一个坐标为1-100的正方形棋盘,在这个正方形上有N个棋子,然后有M个数,表示沿着这个棋盘的M条竖线各切了1刀,将其划分成了M-1个块,这M条竖线中必包含有1和100。现在要求你再将其划分,这次是按横线划分,要求划A次,A次中必要包含1和100,划分完后,将其分解成一个个小矩形,要求含有棋子的矩形数目达到最大

解题思路:预处理比较难,再还没有横着切之前,先判断以下每一块的每一行是否有棋子,因为是按横线划分的,且只要求包含有棋子,所以在同一块中,横线上有多少的棋子就不重要了,然后再判断一下,同一块中从第0行到第i行能划分成几个块,能划分成几个块表示的是有几行有棋子,这是一个闭区间[0,i],最后再统计一下第i行到第j行能划分成几块,这是个左闭右开的区间[i,j),就是统计一下每一块的第[i,j)区间内能被划分成几块,然后再进行相加,这样预处理就好了。

接着是转换公式了:dp[i][j] = max(dp[i][j],dp[k][j-1] + s[i][k]),dp[i][j]表示从区间(i,100)划分j次的最优解,具体的解析请看点击打开链接

我代码中也有注解。

#include<cstdio>#include<cstring>#define maxn 110bool vis[maxn][maxn],judge[maxn][maxn],g[maxn][maxn];int dp[maxn][maxn],s[maxn][maxn],f[maxn][maxn],a[maxn],path[maxn][maxn];int N,A,S;void init() {memset(g,0,sizeof(g));memset(vis,0,sizeof(vis));memset(f,0,sizeof(f));int t1,t2;for(int i = 0; i < N; i++) {scanf("%d%d",&t1,&t2);g[t2][t1] = 1;}scanf("%d",&S);for(int i = 1; i <= S; i++)scanf("%d",&a[i]);//judge[i][k]是判断第k块第i行是否有符合的点,f[i][k]是记录第k块从第0行到第i行可以划分成几个区,区间是[0,i],闭区间scanf("%d",&A);for(int k = 1; k < S; k++) for(int i = 1; i < 100; i++)   {judge[i][k] = f[i][k] = 0;for(int j = a[k]; j < a[k+1]; j++)if(g[i][j] == 1) {judge[i][k] = 1;break;}f[i][k] = judge[i][k];f[i][k] += f[i-1][k];}//s[i][j]记录的是从第i行到第j行有几个区,区间是[i,j),左闭右开的for(int i = 1; i < 100; i++)for(int j = i + 1; j <= 100; j++) {s[i][j] = 0;for(int k = 1; k < S; k++)if(f[j-1][k] - f[i][k] + judge[i][k])s[i][j]++;}}int DP(int i,int j) {int k, ans;if(vis[i][j])return dp[i][j];if(j == 0)return dp[i][j] = s[i][100];vis[i][j] = 1;dp[i][j] = 0;//dp[i][j]表示从(i,100)中划分j次,转移方程为dp[i][j] = max(dp[k][j-1]+s[i][k],dp[i][j]),相当于把第k条线当成了划分线了,就是说上一块的区间是[i,k),还要判断从(k,100)是否还能够划分,能被当成划分的线只剩下100-k-1条了(排除了第k条和第100条),还需要划分j-1次,所以当100-k-1 < j - 1时,就表示不能被划分了,break掉for(k = i + 1; k < 100; k++) {if(100 - k - 1 < j -1)break;ans = DP(k,j-1);if(s[i][k] + ans > dp[i][j]) {dp[i][j] = s[i][k] + ans;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() {while(scanf("%d",&N) != EOF && N != -1) {init();DP(1,A-2);printf("%d",A);printf(" 1");print_path(1,A-2);printf(" 100\n");}return 0;}



0 0