NOIP2010 引水入城 贪心+DFS
我们先把简单的不能搞死,具题意可证:每个蓄水长的管辖区域一定是连续的。
证明:既然我们已经能了那么我们就可以说如果这个区间不是连续的那我们取出这个区间中间阻隔开的那一段,那么对于这一整个区间来说水源不可能来自两边那么一定至少有一条直通蓄水厂的路连其上一点,那么对于连着的那个蓄水厂a,以及我们造成此区间的蓄水厂b,b一定不是a,b若在a的左边则无法到达右边,若b在a的右边则无法到达左边,因此移动是连续的。由于数据中有一个一行的(既邻水又临漠),我要说一下,对于这个结论显然成立。
那么我们既然知道了每个蓄水厂的管辖范围我们进行右端点贪心即可。
#include<cstdio> #include<bitset> #include<algorithm> #include<vector> #define N 505 using namespace std; inline int read() { int sum=0; char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9') { sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar(); } return sum; } struct Tr { int to,next; }c[N*N*4]; int head[N*N],t,kin[N*N]; bool v[N*N]; bitset<N> ctr[N]; int n,m; int h[N][N]; inline int get(int x,int y) { return (x-1)*m+y; } inline void add(int x,int y) { c[++t].to=y; c[t].next=head[x]; head[x]=t; } void Init() { n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) h[i][j]=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(h[i+1][j]<h[i][j]&&i+1<=n)add(get(i,j),get(i+1,j)); if(h[i-1][j]<h[i][j]&&i-1>0)add(get(i,j),get(i-1,j)); if(h[i][j+1]<h[i][j]&&j+1<=m)add(get(i,j),get(i,j+1)); if(h[i][j-1]<h[i][j]&&j-1>0)add(get(i,j),get(i,j-1)); } for(int i=1;i<=m;i++) kin[get(1,i)]=1,kin[get(n,i)]=2; } int hol=0; void jud(int x) { if(v[x])return; if(kin[x]==2)hol++; v[x]=1; for(int i=head[x];i;i=c[i].next) jud(c[i].to); } bool Jud() { for(int i=1;i<=m;i++) jud(i); if(hol<m) { printf("0"); printf("\n"); printf("%d\n",m-hol); return 1; } return 0; } bool over[N]; int vo[N*N]; void dfs(int x,int now) { if(vo[x]==now)return; vo[x]=now; if(kin[x]==1&&x!=now) over[x]=1; if(kin[x]==2) ctr[now][x-(n-1)*m]=1; for(int i=head[x];i;i=c[i].next) dfs(c[i].to,now); } struct DP { int l,r; }dp[N]; int Min,ans,NN=1; int bre[N]; bool Now[N]; int comp(const DP a,const DP b) { return a.l<b.l; } void Dp() { NN--; sort(dp+1,dp+NN+1,comp); int now=1,Ans=1; for(int i=1;i<=NN;i++) { if(dp[i].l>now) { ans++; now=Ans+1; if(now>m)break; } if(dp[i].r>Ans) Ans=dp[i].r; } if(now<=m) ans++; } void work() { for(int i=1;i<=m;i++) if(!over[i]) dfs(i,i); for(int i=1;i<=m;i++) if(!over[i]) bre[++Min]=i; for(int i=1;i<=Min;i++) { int no=0; for(int j=1;j<=m;j++) { if(no==0&&ctr[bre[i]][j]==1) { no=1; dp[NN].l=j; } if(no==1&&ctr[bre[i]][j]==0) { dp[NN].r=j-1; break; } } if(no==0) { i--; Min--; continue; } if(dp[i].r==0) dp[NN].r=m; NN++; } Dp(); printf("1"); printf("\n"); printf("%d\n",ans); } int main() { Init(); if(Jud())return 0; work(); return 0; }
苟利国家生死以, 岂因祸福避趋之。