这道题相当的。。。
先自己想没发现一些特异性(这以后一定要注意)
当然先说一下本题有趣的地方:针对第一排的格子如果周围的格子比他高那么就没必要搜它(当然如果不是记搜的话)
针对满足有解得情况我们很显然的知道了第一排的点的扩散到最后一排一定是一个连续的区间(成功转化成区间覆盖问题)
打了一个暴力bfs 因为感觉像是bfs 但是呢有点缺陷
数组大小暴力的话不判重复可能会很大很大,所以RE到怀疑人生
暴力程序能得80分呢,瞎写的贪心其实和区间覆盖贪心类似。正确性不言而喻、
暴力 violence bf: brute force :
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> #define INF 168430090 #define s1 te.x #define s2 te.y using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar(' ');return; } const int MAXN=502; vector<int> f[MAXN],g[MAXN]; int n,m; int a[MAXN][MAXN],t,h,b[MAXN][MAXN]; int mark[MAXN],cnt=0,ans=0; struct wy { int x,id,y; }q[1000002]; const int dx[4]={0,0,1,-1}; const int dy[4]={1,-1,0,0}; int judge(int x,int y) { if(x>=1&&y>=1&&x<=n&&y<=m)return 1; return 0; } void bfs() { //put(q.size()); while(h++<t) { wy te=q[h]; if(te.x==n){b[te.id][te.y]=1;} for(int i=0;i<4;i++) { int xx=s1+dx[i]; int yy=s2+dy[i]; if(judge(xx,yy)==0)continue; if(a[xx][yy]>=a[s1][s2])continue; wy tn=te; tn.x=xx;tn.y=yy; q[++t]=tn; } } return; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(); for(int i=1;i<=m;i++) { if(a[1][i-1]>a[1][i]||a[1][i+1]>a[1][i])continue;//优化 wy x; x.x=1;x.id=i;x.y=i; q[++t]=x; } bfs(); for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) if(b[i][j]==1)f[i].push_back(j); for(int i=1;i<=m;i++) for(unsigned int j=0;j<f[i].size();j++) g[f[i][j]].push_back(i); /*for(int i=1;i<=m;i++) { for(int j=0;j<g[i].size();j++)cout<<g[i][j]<<' '; cout<<endl; }*/ for(int i=1;i<=m;i++) { if(mark[i]==1)continue; int sum=0,maxx=0,t=-1; for(unsigned int j=0;j<g[i].size();j++) { for(unsigned int k=0;k<f[g[i][j]].size();k++) { int tn=f[g[i][j]][k]; if(mark[tn]==1)continue; sum++; } if(sum>maxx)maxx=sum,t=g[i][j]; } if(t<0)continue; for(unsigned int k=0;k<f[t].size();k++) { int tn=f[t][k]; if(mark[tn]==1)continue; mark[tn]=1; cnt++; } ans++; } if(cnt!=m)put(0),put(m-cnt); else put(1),put(ans); return 0; }
当然这个bf是瞎写的没看题解呢
看了题解之后才知道自己没看出一些特异性的特点 最后满足条件的一定是一整段区间
那么记忆化搜索顺理成章出来了。当然我在写记搜的时候是采用有返回值得dfs
一个左边一个右边两个值那么我怎么返回2个值呢,考虑结构体类型的dfs
写了一半感觉有点恶心所以看了题解的记搜(我太菜了记搜都不会写!)
采用的是标记法不需要返回值这个也比较简单。值得一提的是关于这道题的区间覆盖问题
dp的复杂度较高 而我的大贪心 则很快哩。
值得再提的是oj 老了 会堆栈溢出需要加一个 汇编开栈 正常Windows给栈的空间是8MB我生生又多开了20MB!
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> #define INF 168430090 #define s1 t[i].x #define s2 t[i].y using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar(' ');return; } //引水进城 30分做法 //发现问题特异性 但是bfs 超时N次 //考虑一发记搜 //记搜也要考虑好方式我跪了 const int MAXN=502; const int dx[4]={0,0,1,-1}; const int dy[4]={1,-1,0,0}; int l[MAXN][MAXN],r[MAXN][MAXN];//直接记搜返回区间貌似 结构体不太行换数组使用 int n,m,vis1[MAXN][MAXN]; int a[MAXN][MAXN],vis[MAXN],cnt=0; int f[MAXN][MAXN],d[MAXN];//d[i]表示1~i这个区间被最少线段覆盖的数量 int sum=0,ans=0,now=1; struct wy { int x,y; }t[MAXN]; int judge(int x,int y) { if(x>=1&&y>=1&&x<=n&&y<=m)return 1; return 0; } void dfs(int x,int y) { if(vis1[x][y]!=0)return; if(x==n){l[x][y]=min(l[x][y],y);r[x][y]=max(r[x][y],y);vis[y]=1;} for(int i=0;i<4;i++) { int xx=dx[i]+x; int yy=dy[i]+y; if(xx<=0||yy<=0||xx>n||yy>m)continue; if(a[xx][yy]>=a[x][y])continue; if(vis1[xx][yy]==0)dfs(xx,yy); vis1[xx][yy]=1; l[x][y]=min(l[x][y],l[xx][yy]); r[x][y]=max(r[x][y],r[xx][yy]); } return; } int main() { freopen("1.in","r",stdin); int __size__ = 20 << 20; // 20MB char *__p__ = (char*)malloc(__size__) + __size__; __asm__("movl %0, %%esp " :: "r"(__p__)); memset(l,10,sizeof(l)); memset(r,0,sizeof(r)); memset(vis,0,sizeof(vis)); memset(vis1,0,sizeof(vis1)); memset(f,10,sizeof(f)); memset(d,10,sizeof(d)); n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)a[i][j]=read(); for(int i=1;i<=m;i++) { if(a[1][i-1]>a[1][i]||a[1][i+1]>a[1][i])continue; if(vis1[1][i]==1)continue; dfs(1,i); } for(int i=1;i<=m;i++)if(vis[i]==0)cnt++; if(cnt!=0)put(0),put(cnt);//30分成功到手 else//70分考虑dp或者考验一下贪心 { for(int i=1;i<=m;i++)t[i].x=l[1][i],t[i].y=r[1][i]; //for(int i=1;i<=m;i++)cout<<t[i].x<<' '<<t[i].y<<endl; //贪心: 贪心大法好 最坏m^2 while(now<=m) { int maxx=0; for(int i=1;i<=m;i++) { if(s1<=now)maxx=max(maxx,s2); } now=maxx+1;ans++; } put(1);put(ans); //dp 一下 看了一下dp也能过但是不建议使用dp 复杂度最坏高达m^3 卡常不好的话会T呢 /*for(int i=1;i<=m;i++) for(int j=t[i].x;j<=t[i].y;j++) for(int k=j;k<=t[i].y;k++) f[j][k]=1; d[0]=0; for(int i=1;i<=m;i++) for(int j=0;j<i;j++) d[i]=min(d[i],d[j]+f[j+1][i]); put(1);put(d[m]);*/ } return 0; }
新学期我需要静下心来思考。