T1:
直接模拟,详见代码注释。
复杂度$O(NM)$。
Code:
#include<iostream> #include<cstdio> #include<vector> #include<queue> using namespace std; const int N=1010; const int M=10010; int n,m,tot=0; int a[N][N]; int u[M],d[M],l[M],r[M],bl[M];//上下左右边界及水箱编号 vector<int> v[M],bx[N],by[N];//v做临接表,bx和by存储水箱边界 int get() { char c=getchar(); while(c!='.'&&c!='-'&&c!='|'&&c!='+'&&(c<'0'||c>'9')) c=getchar(); if(c>='0'&&c<='9') return c-'0';//水箱编号 else if(c=='-') return -1;//横向管道 else if(c=='|') return -2;//纵向管道 else if(c=='+') return -3;//管道转折,其实和前两个一样,可以不做区分 else return -4;//空格子 } void find(int id,int x,int y)//二分查找水箱边界 { int xx,yy; yy=lower_bound(bx[x].begin(),bx[x].end(),y)-bx[x].begin(); r[id]=bx[x][yy];l[id]=bx[x][yy-1]; xx=lower_bound(by[y].begin(),by[y].end(),x)-by[y].begin(); d[id]=by[y][xx];u[id]=by[y][xx-1]; } void clean(int id)//将整个水箱的区域都标上该水箱的编号 { for(int i=u[id];i<=d[id];i++){ for(int j=l[id];j<=r[id];j++) a[i][j]=id; } } int walk(int x,int y)//沿管道寻找 { a[x][y]=-4; if(a[x+1][y]>0) return a[x+1][y];//水箱成树形 if(a[x+1][y]<=-1&&a[x+1][y]>=-3) return walk(x+1,y); if(a[x][y+1]<=-1&&a[x][y+1]>=-3) return walk(x,y+1); if(a[x][y-1]<=-1&&a[x][y-1]>=-3) return walk(x,y-1); } void work(int id) { for(int i=d[id];i>=u[id];i--){//水必定先进入靠下的水箱,所以从下到上枚举 if(a[i][l[id]-1]<=-1&&a[i][l[id]-1]>=-3) v[id].push_back(walk(i,l[id]-1)); if(a[i][r[id]+1]<=-1&&a[i][r[id]+1]>=-3) v[id].push_back(walk(i,r[id]+1)); } } void print(int id)//按水箱高度递归输出 { for(int i=0;i<v[id].size();i++) print(v[id][i]); printf("%d ",id); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ a[i][j]=get();//获取格子类型 if(a[i][j]>=0&&a[i][j-1]>=0){//注意水箱编号大于一位的情况 a[i][j]+=10*a[i][j-1];//用类似快读的思想 a[i][j-1]=-4;//每个水箱内只能有一个数字 } if(a[i][j]>=-3&&a[i][j]<=-1){//将水箱边界存入 bx[i].push_back(j); by[j].push_back(i); } } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(a[i][j]>=0){ bl[++tot]=a[i][j];//记录水箱编号,水箱编号可能不连续 find(a[i][j],i,j);//寻找水箱边界 } } } for(int i=1;i<=tot;i++) clean(bl[i]); for(int i=1;i<=tot;i++) work(bl[i]); print(1); return 0; }
T2:
DP好题,不过暴力也能A。
题目大意:一条线上N个点,共有精灵M个,时间为一时位于K,精灵都有价值,但一段时间后会消失,求收获的最大权值。
DP(正解):
搜索(暴力):
暴力压正解,我就不说什么了…………
下面说DP:搜索可以自己想
设DP数组f[i][j][k],表示i~j已经走过,目前位于i的最大值。
由于抓取精灵不需要时间,而且权值均为正,所以每次遇到精灵一定会抓。
先对精灵按照坐标排序。
将精灵的坐标连同起始点离散,初始化为负无穷,时间为一时起点处权值赋为0。
这是一个区间DP,先枚举时间,然后枚举区间长度,再枚举左端点,算出右端点。
然后我们就可以开心地DP了:
i为时间,L为左端点,R为右端点,val代表精灵的权值,p代表精灵的位置,t为精灵消失的时间。
f[L-1][R][i+1]=max(f[L-1][R][i+1],f[L][R][i]+val[L-1]) (i+p[L]-p[L-1]<=t[L-1])
f[L-1][R][i+1]=max(f[L-1][R][i+1],f[L][R][i]) (i+p[L]-p[L-1]>t[L-1])
f[R+1][L][i+1]=max(f[R+1][L][i+1],f[R][L][i]+val[R+1]) (i+p[R+1]-p[R]<=t[R+1])
f[R+1][L][i+1]=max(f[R+1][L][i+1],f[R][L][i]) (i+p[R+1]-p[R]>t[R+1])
f[R+1][L][i+1]=max(f[R+1][L][i+1],f[L][R][i]+val[R+1]) (i+p[R+1]-p[L]<=t[R+1])
f[R+1][L][i+1]=max(f[R+1][L][i+1],f[L][R][i]) (i+p[R+1]-p[L]>t[R+1])
f[L-1][R][i+1]=max(f[L-1][R][i+1],f[R][L][i]+val[L-1]) (i+p[R]-p[L-1]<=t[L-1])
f[L-1][R][i+1]=max(f[L-1][R][i+1],f[R][L][i]) (i+p[R]-p[L-1]>t[L-1])
后四行的转移方程代表从区间的一头走到另一头再扩展区间,不能丢。
起点算作一只贡献为0的精灵,方便判断。
注意判断在该时间内精灵是否已消失。
DP过程中不断对ans取max,最后的max即为答案。
共有M只精灵,时间的最大值为T。
时间复杂度$O(M^2T)$
Code:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 const int N=2010; 6 const int M=110; 7 const int inf=9999999; 8 int n,k,m,t=0,ans=0; 9 int s[M],dp[M][M][N]; 10 struct point { 11 int a,b,c; 12 }p[M]; 13 bool comp(const point a1,const point a2) 14 { 15 return a1.a<a2.a; 16 } 17 int main() 18 { 19 scanf("%d%d%d",&n,&k,&m); 20 for(int i=1;i<=m;i++){ 21 scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c); 22 t=max(p[i].c,t); 23 } 24 p[++m].a=k; 25 sort(p+1,p+m+1,comp); 26 for(int i=1;i<=t;i++){ 27 for(int j=1;j<=m;j++){ 28 for(int l=1;l<=m-j+1;l++){ 29 int r=l+j-1; 30 dp[l][r][i]=dp[r][l][i]=-inf; 31 } 32 } 33 } 34 for(int i=1;i<=m;i++){ 35 s[i]=p[i].a;p[i].a=i; 36 if(s[i]==k) 37 dp[i][i][1]=p[i].b; 38 } 39 for(int i=1;i<=t;i++){ 40 for(int j=1;j<=m;j++){ 41 for(int l=1;l<=m-j+1;l++){ 42 int r=l+j-1; 43 if(dp[l][r][i]>=0){ 44 if(l>=2){ 45 int ti=i+s[l]-s[l-1]; 46 if(ti<=p[l-1].c) 47 dp[l-1][r][ti]=max(dp[l-1][r][ti],dp[l][r][i]+p[l-1].b); 48 else 49 dp[l-1][r][ti]=max(dp[l-1][r][ti],dp[l][r][i]); 50 ans=max(ans,dp[l-1][r][ti]); 51 } 52 if(r<=m-1){ 53 int ti=i+s[r+1]-s[l]; 54 if(ti<=p[r+1].c) 55 dp[r+1][l][ti]=max(dp[r+1][l][ti],dp[l][r][i]+p[r+1].b); 56 else 57 dp[r+1][l][ti]=max(dp[r+1][l][ti],dp[l][r][i]); 58 ans=max(ans,dp[r+1][l][ti]); 59 } 60 } 61 if(dp[r][l][i]>=0){ 62 if(l>=2){ 63 int ti=i+s[r]-s[l-1]; 64 if(ti<=p[l-1].c) 65 dp[l-1][r][ti]=max(dp[l-1][r][ti],dp[r][l][i]+p[l-1].b); 66 else 67 dp[l-1][r][ti]=max(dp[l-1][r][ti],dp[r][l][i]); 68 ans=max(ans,dp[l-1][r][ti]); 69 } 70 if(r<=m-1){ 71 int ti=i+s[r+1]-s[r]; 72 if(ti<=p[r+1].c) 73 dp[r+1][l][ti]=max(dp[r+1][l][ti],dp[r][l][i]+p[r+1].b); 74 else 75 dp[r+1][l][ti]=max(dp[r+1][l][ti],dp[r][l][i]); 76 ans=max(ans,dp[r+1][l][ti]); 77 } 78 } 79 } 80 } 81 } 82 printf("%d ",ans); 83 return 0; 84 }
建议搜索AC的人打一遍DP,毕竟正经NOIP数据是不会这么水的。