1. 程式人生 > >luoguP1514 引水入城 題解(NOIP2010)

luoguP1514 引水入城 題解(NOIP2010)

org == new std 最優 else 水流 define math

P1514 引水入城 題目

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<ctime>
#define ll long long
#define rg register
#define N 550
using namespace std;

int n,m; int dx[4]={1,0,-1,0}; int dy[4]={0,1,0,-1}; ll map[N][N];//記錄每個城市的高度 bool b[N][N],ans[N][N];//b[][]記錄這個城市是否流過了(我也不知道有不有用) //ans[i][j]表示臨河的第i個城市通過......可以讓沙漠第j個城市有水(有水為true,沒水為false) ll s[N],ss[N];//s[]用於儲存沙漠中第i個城市可以由幾個城市引水 //ss[]用於暫時存一下s[]中的情況,見代碼吧 struct water{ int
x,y; }ljl[1000050];//bfs的隊列,水流到第[x][y]城市了 inline int read() { int s=0,m=1;char ch=getchar(); while(ch!=-&&(ch<0||ch>9))ch=getchar(); if(ch==-)m=-1,ch=getchar(); while(ch>=0&&ch<=9)s=(s<<3)+(s<<1)+ch-0,ch=getchar(); return s*m; } void Bfs() {
for(int k=1;k<=n;++k) { if(map[1][k]<map[1][k-1]||map[1][k]<map[1][k+1])//這個很重要!!! continue; //剪枝,如果兩邊都比它高,它也就沒用了 memset(b,0,sizeof(b)); int head=0,tail=1;//套一波廣搜模板 ljl[1].x=1,ljl[1].y=k; if(ljl[1].x==m)ans[k][k]=1;//河和沙漠挨在一坨 while(head<tail) { ++head; for(int i=0;i<4;++i) { int nx=ljl[head].x+dx[i],ny=ljl[head].y+dy[i]; if(nx>=1&&nx<=m&&ny>=1&&ny<=n&&!b[nx][ny])//邊界 { if(map[nx][ny]<map[ljl[head].x][ljl[head].y])//條件 { ++tail,b[nx][ny]=1; ljl[tail].x=nx,ljl[tail].y=ny; if(nx==m) { ans[k][ny]=1; } } } } } } } bool check() //用來檢查這種開貯水池方法是否行得通 { for(int i=1;i<=n;++i) { if(!s[i])return 0; } return 1; } void copy(int hh) { if(hh)//保存一下合法但不一定最優的答案 for(int i=1;i<=n;++i) { ss[i]=s[i]; } else//減去城市後發現不合法了肯定會到剛剛的方案再去尋找 for(int i=1;i<=n;++i) { s[i]=ss[i]; } } void jian(int kk) //減去一個貯水池 { for(int i=1;i<=n;++i) { s[i]-=ans[kk][i]; } } int main() { m=read();n=read(); for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) map[i][j]=read(); Bfs(); for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { //先假設我在河邊上每個城市都要開貯水池 s[j]+=ans[i][j]; } } if(!check())//去檢查是否所有沙漠城市都有水了 { int z=0; puts("0"); for(int i=1;i<=n;++i) { if(s[i]==0)++z;//沒水就計入答案 } printf("%d\n",z); return 0; } puts("1"); int answer=n;//假設貯水池全都要 for(int i=1;i<=n;++i) { if(map[1][i]<map[1][i-1]||map[1][i]<map[i][i+1])//同bfs裏的剪枝原理一樣 { answer--;continue; } copy(1);//儲存一下此時的答案 jian(i);//試一試減去這個城市的貯水池 if(!check())//判斷是否還合法 copy(0);//不合法就不能減,回到不減之前 else answer--;//合法就減去這個(半個貪心吧!) } printf("%d\n",answer); return 0; }

luoguP1514 引水入城 題解(NOIP2010)