1. 程式人生 > >luogu 1066 引水入城(bfs+貪心)

luogu 1066 引水入城(bfs+貪心)

lap nod cstring 關鍵字 true 選擇 vector ++ ||

90分,有一個點TLE....

首先可以證明一個東西,如果從上面一排的某個點bfs一次到最下面一排的飲水點不是一個區間的話,那麽最後一定所有飲水點不會被覆蓋完的。

證明考慮反證法。

所以從上面一排的每個點bfs一次得到一個區間。題目轉化為給出m個區間覆蓋m個點的最小區間選擇數。

顯然是個明顯的貪心,以左區間端點為第一關鍵字升序排序,右區間端點為第二關鍵字降序排序,那麽每次貪心的選擇一個覆蓋最大的區間即可。

時間復雜度O(n*m^2+mlogm).需要常數優化。

技術分享
# include <cstdio>
# include <cstring>
# include <cstdlib>
# include 
<iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-7 # define MOD 1024523
# define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int
> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0;char ch=getchar(); while(ch<0||ch>9){ch=getchar();} while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();} return x; } void Out(int a) { if(a<0) {putchar(-); a=-a;} if(a>=10) Out(a/10); putchar(a%10+0); } const int N=505; //Code begin... int G[N][N], ps[4][2]={1,0,-1,0,0,1,0,-1}; bool vis[N][N], mark[N], flag=true; struct Node{int l, r;}node[N]; queue<PII>que; bool comp(Node a, Node b){ if (a.l!=b.l) return a.l<b.l; return a.r>b.r; } int main () { int n, m, dx, dy; PII tmp; n=Scan(); m=Scan(); FOR(i,1,n) FOR(j,1,m) G[i][j]=Scan(); FOR(i,1,m) { mem(vis,false); que.push(mp(1,i)); vis[1][i]=true; while (!que.empty()) { tmp=que.front(); que.pop(); FO(j,0,4) { dx=tmp.first+ps[j][0], dy=tmp.second+ps[j][1]; if (dx<1||dy<1||dx>n||dy>m||vis[dx][dy]||G[tmp.first][tmp.second]<=G[dx][dy]) continue; vis[dx][dy]=true; que.push(mp(dx,dy)); } } FOR(j,1,m) { if (vis[n][j]) { mark[j]=true; if (flag) { if (!vis[n][j-1]) { if (node[i].l) flag=false; else node[i].l=j; } if (!vis[n][j+1]) node[i].r=j; } } } } int ans=0; FOR(i,1,m) if (!mark[i]) ++ans; if (ans) printf("0\n%d\n",ans); else { sort(node+1,node+m+1,comp); int now=1; while (node[now].r==0) ++now; int ma=node[now].r, r; ++now; ++ans; while (now<=m) { r=ma; if (r==m) break; while (now<=m&&node[now].l<=r+1) ma=max(ma,node[now].r), ++now; ++ans; } printf("1\n%d\n",ans); } return 0; }
View Code

luogu 1066 引水入城(bfs+貪心)