zoukankan      html  css  js  c++  java
  • Luogu1514 引水入城

    题意比较绕,多看两遍就看懂了

    重点在于求满足要求的情况时的答案

    可能也是可想的

    注意到对于一个第一行的点,如果是满足要求的情况下,
    他所能到达的一定是一段连续的区间
    考虑如果不连续的话,那一定是绕过了一个区域,
    那这一圈都是可以流过去的,而且这一圈都不能流进这个区域
    那他就是一个不满足要求的情况

    所以对于一个点,他能给供水的是一段区间

    那这就转化成了一个线段覆盖问题

    正好之前也没做过,这里写一下:

      有两种写法,

      第一种是贪心:在当前已经选出的区间中找一条左端点在其中,
             且右端点最大的线段,之后将这条线段并入已选区间
             直至选完

      第二种是 dp:f[i] 表示到第 i 个位置最少用 f[i] 条线段覆盖
             转移显然是从一段的最小转移,由于要保证最小,
             就用线段树维护区间最小就行了
             转移的位置只要保证能跟之前的拼起来就行

    由于比较懒就写了贪心。。。

    需要注意的是贪心的话你要保证跟之前区间接起来的话,是已选区间右端点 + 1就行


     代码:

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    #include <cctype>
    #include <cstdio>
    #include <queue>
    using namespace std;
    
    const int MAXN = 505, MAXM = 505;
    
    struct POS{
    	int x, y;
    	POS(int X = 0, int Y = 0) {x = X; y = Y;}
    };
    struct LINE{
    	int l, r;
    	LINE(int L = -1, int R = -1) {l = L; r = R;}
    	bool operator < (const LINE& b) const {
    		return ((l == b.l) ? (r > b.r) : (l < b.l));
    	}
    	inline void update(LINE val) {
    		if (~val.l) {
    			l = ((~l) ? min(l, val.l) : val.l);
    			r = max(r, val.r);
    		}
    		return;
    	}
    }line[MAXN][MAXM], myb[MAXM];
    int n, m, totok;
    int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
    int mp[MAXN][MAXM];
    bool vis[MAXN][MAXM];
    queue<POS> q;
    
    inline void chk() {
    	for (int i = 1; i <= m; ++i) {
    		q.push(POS(1, i));
    		vis[1][i] = true;
    	}
    	while (!q.empty()) {
    		POS cur = q.front(); q.pop();
    		for (int i = 0; i < 4; ++i) {
    			int tx = cur.x + dx[i], ty = cur.y + dy[i];
    			if (tx < 1 || tx > n || ty < 1 || ty > m || vis[tx][ty] || mp[cur.x][cur.y] <= mp[tx][ty]) continue;
    			vis[tx][ty] = true;
    			q.push(POS(tx, ty));
    		}
    	}
    	return;
    }
    LINE dfs(POS cur, POS frm) {
    	if (cur.x == n) {
    		line[cur.x][cur.y].update(LINE(cur.y, cur.y));
    	}
    	for (int i = 0; i < 4; ++i) {
    		int tx = cur.x + dx[i], ty = cur.y + dy[i];
    		if (tx < 1 || tx > n || ty < 1 || ty > m || (tx == frm.x && ty == frm.y) || mp[cur.x][cur.y] <= mp[tx][ty]) continue;
    		line[cur.x][cur.y].update(dfs(POS(tx, ty), cur));
    	}
    	return line[cur.x][cur.y];
    }
    inline void getans() {
    
    	for (int i = 1; i <= m; ++i) 
    		myb[i] = line[1][i];
    	sort(myb + 1, myb + m + 1);
    	LINE curitv;
    	int ans = 1, ptr = 1;
    	while (!(~myb[ptr].l)) ++ptr;
    	curitv = myb[ptr];
    	++ptr;
    	while (curitv.r < m) {
    		register int curmax = 0;
    		while (ptr <= m && myb[ptr].l <= curitv.r + 1) {
    			curmax = max(curmax, myb[ptr].r);
    			++ptr;
    		}
    		curitv.r = curmax;
    		++ans;
    	}
    	printf("1
    %d
    ", ans);
    	return;
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; ++i) 
    		for (int j = 1; j <= m; ++j) 
    			scanf("%d", &mp[i][j]);
    	chk();
    	bool GG = false;
    	for (int i = 1; i <= m; ++i) 
    		if (!vis[n][i]) {
    			GG = true;
    			++totok;
    		}
    	if (GG) {
    		printf("0
    %d
    ", totok);
    		return 0;
    	}
    	for (int i = 1; i <= m; ++i) 
    		dfs(POS(1, i), POS(0, 0));
    	getans();
    	return 0;
    }
  • 相关阅读:
    《Linux C编程一站式学习》第5章深入理解函数课后作业
    《Linux C编程一站式学习》——常量、变量和表达式notes
    《Linux C编程一站式学习》——第一个程序HelloWorld.c
    二分排序java实现
    leetcode初级算法(数组)——从数组中删除重复项
    利用层次遍历原理构建二叉树
    canvas学习笔记:绘制各种图形
    获取字符串长度【把双字节的替换成两个单字节的然后再获得长度
    oracle PLSQL程序造数据笔记
    oracle取一条记录中多个列的最大值和最小值
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9839637.html
Copyright © 2011-2022 走看看