第二周 7.26-8.1
7.26
多校第一场1006的一篇题解看了好几天了。
复习了LCA。只会离线的。
想了很久其中的状态转移。
以及dfs序和求和的方法。
从昨天下午开始码。今天终于码(抄)好了。
一会补在多校那篇里。
先补个BC。
HDU 5312 Sequence
按照官方题解。
先看能不能一个。我lower_bound找的。
然后拆两个的时候不能太暴力。会T的。
从两边线性找是可以过的。(然而时间也蛮多。
3-8的情况方便了。
感觉时间主要在2上。目前也不会更快的方法。
1 # include <iostream> 2 # include <cstdio> 3 # include <algorithm> 4 using namespace std; 5 int a[20000]; 6 7 int main(void) 8 { 9 int n; 10 for(int i=0;;i++) 11 { 12 a[i]=3*i*(i-1)+1; 13 if(a[i]>1000000000) {n=i;break;} 14 } 15 int T; cin>>T; 16 while(T--) 17 { 18 int m; scanf("%d",&m); 19 int x=lower_bound(a,a+n,m)-a; 20 if(a[x]==m) {printf("1\n"); continue;} 21 int ans=-1,s=1,t=n-1; 22 while(s<=t) 23 { 24 if(a[s]+a[t]==m) {ans=2;break;} 25 if(a[s]+a[t]>m) t--; 26 if(a[s]+a[t]<m) s++; 27 } 28 if(ans>0) {printf("%d\n",ans); continue;} 29 for(int i=3;i<10;i++)if((m-i)%6==0){ans=i;break;} 30 printf("%d\n",ans); 31 } 32 return 0; 33 }
学最大流的EK算法。
7.27
HDU 1532 Drainage Ditches
最大流模板题。EK过。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <queue> 6 using namespace std; 7 # define maxn 210 8 int N,M,map[maxn][maxn],pre[maxn],flow[maxn]; 9 10 int BFS(void) 11 { 12 memset(pre,0,sizeof(pre)); 13 queue<int> q; q.push(1); 14 flow[1]=2147483647; 15 while(!q.empty()) 16 { 17 int tem=q.front(); q.pop(); 18 if(tem==M) break; 19 for(int i=2;i<=M;i++) 20 { 21 if(!pre[i]&&map[tem][i]>0) 22 { 23 flow[i]=min(flow[tem],map[tem][i]); 24 q.push(i); pre[i]=tem; 25 } 26 } 27 } 28 if(!pre[M]) return 0; 29 return flow[M]; 30 } 31 32 int max_flow(void) 33 { 34 int ret=0,f; 35 while(f=BFS()) 36 { 37 ret+=f; 38 int pos=M; 39 while(pos!=1) 40 { 41 int tem=pre[pos]; 42 map[tem][pos]-=f; 43 map[pos][tem]+=f; 44 pos=tem; 45 } 46 } 47 return ret; 48 } 49 50 int main(void) 51 { 52 while((scanf("%d%d",&N,&M))!=EOF) 53 { 54 memset(map,0,sizeof(map)); 55 for(int i=1;i<=N;i++) 56 { 57 int S,E,C; scanf("%d%d%d",&S,&E,&C); 58 map[S][E]+=C; 59 } 60 printf("%d\n",max_flow()); 61 } 62 return 0; 63 }
在补多校1的1007。EK是T的。
于是顺便看了下SAP。
感觉相当于在EK的BFS里加了距离标记。保证找到的是最短路径。
再加上了一个gap的优化。
神奇的是初始化的时候可以把距离标记置零。过程中会自动调整。
贴个板子。
1 int pre[maxn],level[maxn],gap[maxn]; 2 int SAP(int s,int t,int N)//源点s 汇点t 点数N 3 { 4 memset(pre,0,sizeof(pre));//pre记录增广路的前向弧 5 memset(pre,0,sizeof(level));//level距离标记 6 memset(pre,0,sizeof(gap)); //gap[i]为距离标记为i的点数 7 gap[0]=N; pre[s]=s;//初始化所有点的标记都是0 8 int pos=s,flow=0,aug; 9 while(level[s]<N)//level[s]=N时无法增广 10 { 11 int to; 12 for(to=1;to<=N;to++) 13 if(map[pos][to]>0&&level[to]==level[pos]+1)//寻找可行路 14 break; 15 if(to<=N) 16 { 17 pre[to]=pos; pos=to; 18 if(pos==t)//找到s到t的可行路 更新残网 19 { 20 aug=INF; 21 for(int i=pos;i!=s;i=pre[i]) aug=min(aug,map[pre[i]][i]); 22 flow+=aug; 23 for(int i=pos;i!=s;i=pre[i]) 24 { 25 map[pre[i]][i]-=aug; 26 map[i][pre[i]]+=aug; 27 } 28 pos=s;//重新从s开始寻找可行路 29 } 30 } 31 else//找不到可行路 更新距离标记 32 { 33 int minlevel=N; 34 for(int i=1;i<=N;i++) 35 if(map[pos][i]>0) 36 minlevel=min(minlevel,level[i]); 37 gap[level[pos]]--; 38 if(!gap[level][pos]) break;//出现断层 无可行路 39 level[u]=minlevel+1; 40 gap[level[u]]++; 41 pos=pre[pos]; 42 } 43 } 44 return flow; 45 }
7.28
上午补多校1的1009。
要在线的LCA。于是学了一下倍增法。
Link:http://www.cnblogs.com/OUSUO/p/3805715.html
下午多校。这次终于有能交的题了。
但是感觉做的效果并不好。签完三题就在浪费时间。
最后开1010没码完。赛后看题解发现一个地方想错。
补题吧。
7.29
补多校。
一个线段树敲不对。
搬迁。
7.30
补多校3 1001/1008。
多校弃疗。
一直到晚上还没敲出1009。
后来问了别人思路。
7.31
补多校4 1009/1010/1012。
这场就补三个其他放着先。
明天开始刷专题。
今天不小心发现memset并不像想象的那么快。
T大时真的要看清是不是必须清空阿。
8.1
做背包已是一个月前。忘光了。重新从0-1开始。
HDU 2602 Bone Collector
滚动数组适用于某一维度的状态只依赖于上一状态的情况。可以把空间降一维。
需要注意的是更新的时候要注意顺序。否则容易出现把依赖的状态先更新的情况。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 using namespace std; 6 int dp[1010],v[1010],w[1010]; 7 8 int main(void) 9 { 10 int T; cin>>T; 11 while(T--) 12 { 13 int N,V; scanf("%d%d",&N,&V); 14 for(int i=1;i<=N;i++) scanf("%d",v+i); 15 for(int i=1;i<=N;i++) scanf("%d",w+i); 16 memset(dp,0,sizeof(dp)); 17 for(int i=1;i<=N;i++) 18 for(int j=V;j>=w[i];j--) 19 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 20 printf("%d\n",dp[V]); 21 } 22 return 0; 23 }
背包问题里面有一类能不能达到的问题。(就是只有背包装满才是可行解)
以一般的求价值最大的背包为例。
初始化的时候把dp[0]置0。其他均为负无穷。
这样一来从dp[0]推出的dp值是正常的。代表可行解。
从其他dp推出的值无论怎样都是负数。表示无法达到装满的状态。
HDU 1114 Piggy-Bank
之前看过挺多完全背包的文章。
每个都会先从0-1背包过渡到完全背包。
先用0-1背包的方法逐步优化。最后引出一种O(VN)的方法。
我觉得在理解滚动数组的基础上这种方法不难理解。
直接写这种方法了。
题中要用到上面说到的装满背包的处理方法。
有一点不同在于问题中要求解的是最小值。
所以初始化除dp[0]外应为正无穷。
1 # include <iostream> 2 # include <cstdio> 3 # include <algorithm> 4 using namespace std; 5 # define maxn 505 6 # define maxV 10010 7 # define INF 1000000000 8 int p[maxn],w[maxn],dp[maxV]; 9 10 int main(void) 11 { 12 int T; cin>>T; 13 while(T--) 14 { 15 int E,F,N; 16 scanf("%d%d%d",&E,&F,&N); 17 int V=F-E; 18 for(int i=1;i<=N;i++) scanf("%d%d",p+i,w+i); 19 dp[0]=0; 20 for(int i=1;i<=V;i++) dp[i]=INF; 21 for(int i=1;i<=N;i++) 22 for(int j=w[i];j<=V;j++) 23 dp[j]=min(dp[j],dp[j-w[i]]+p[i]); 24 if(dp[V]<INF) printf("The minimum amount of money in the piggy-bank is %d.\n",dp[V]); 25 else printf("This is impossible.\n"); 26 } 27 return 0; 28 }
HDU 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 (用这么沉重的话题出题真的好吗)
多重背包就是用二分的方法把每种物品拆分。
然后转化成0-1背包问题。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 using namespace std; 6 int v[1000],w[1000],dp[101]; 7 8 int main(void) 9 { 10 int T; cin>>T; 11 while(T--) 12 { 13 int n,m; 14 scanf("%d%d",&n,&m); 15 int cnt=0; 16 for(int i=1;i<=m;i++) 17 { 18 int p,h,c; 19 scanf("%d%d%d",&p,&h,&c); 20 int t=1; 21 while(c>=t) 22 { 23 w[++cnt]=t*p; 24 v[cnt]=t*h; 25 c-=t; 26 t*=2; 27 } 28 if(c) 29 { 30 w[++cnt]=c*p; 31 v[cnt]=c*h; 32 } 33 } 34 memset(dp,0,sizeof(dp)); 35 for(int i=1;i<=cnt;i++) 36 for(int j=n;j>=w[i];j--) 37 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 38 printf("%d\n",dp[n]); 39 } 40 return 0; 41 }
对于混合背包问题。
如果只有0-1背包和完全背包混。
这两个的写法只有循环最内层增减相反的差别。
如果加上多重背包。则把多重化为0-1。
然后就和上面一样了。
暂时没有找到题目。不过情况不复杂。
HDU 2159 FATE
当费用约束为二维的时候。再给dp加上一维即可。
依旧还是什么背包按什么背包写。
这个题的增加约束是物品数目。
所以看成0-1背包或者完全背包都是可以的。
但是觉得这个题目这样做不好。因为到最后还要搜一遍最小的花费。
纯当练手。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 using namespace std; 6 int dp[110][110],a[110],b[110]; 7 8 int main(void) 9 { 10 int n,m,k,s; 11 while((scanf("%d%d%d%d",&n,&m,&k,&s))!=EOF) 12 { 13 for(int i=1;i<=k;i++) scanf("%d%d",a+i,b+i); 14 memset(dp,0,sizeof(dp)); 15 for(int i=1;i<=k;i++) 16 for(int j=1;j<=s;j++) 17 for(int l=m;l>=b[i];l--) 18 dp[j][l]=max(dp[j][l],dp[j-1][l-b[i]]+a[i]); 19 if(dp[s][m]<n) {printf("-1\n");continue;} 20 int ans=m; 21 for(int i=0;i<=s;i++) 22 for(int j=0;j<=m;j++) 23 if(dp[i][j]>=n) 24 ans=min(ans,j); 25 printf("%d\n",m-ans); 26 } 27 return 0; 28 }
HDU 1712 ACboy needs your help
分组背包。 每组最多取一个。
按组递推。每组有不取或者取一个两种选择。
这样思考的话其实是0-1背包的方法。
所以滚动数组的时候容量要递减着推。
【下午题目看错真是坑死。】
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 using namespace std; 6 int map[110][110],dp[110]; 7 8 int main(void) 9 { 10 int n,m; 11 while((scanf("%d%d",&n,&m))!=EOF) 12 { 13 if(!n&&!m) break; 14 for(int i=1;i<=n;i++) 15 for(int j=1;j<=m;j++) 16 scanf("%d",&map[i][j]); 17 memset(dp,0,sizeof(dp)); 18 for(int i=1;i<=n;i++) 19 for(int j=m;j>=0;j--) 20 for(int k=1;k<=j;k++) 21 dp[j]=max(dp[j],dp[j-k]+map[i][k]); 22 printf("%d\n",dp[m]); 23 } 24 return 0; 25 }
晚上打了个BC。
第一题水果。
第二题抄了个板子。时间快到了才码完。然而WA。
HDU 5339 Untitled
总共二十个数。暴搜差不多是1e6情况。1s勉强。
但是很容易想到不会取大于被除数的c。
这里就能剪掉很多。
应该还可以加其他优化。例如去重。或者存在一个数是另一个数的因子之类。
用的BFS。找到的第一个就是ans。0ms。
1 # include <iostream> 2 # include <cstdio> 3 # include <queue> 4 # include <algorithm> 5 using namespace std; 6 typedef pair<int,int> pii; 7 8 int main(void) 9 { 10 int T; cin>>T; 11 while(T--) 12 { 13 int n,a,num[21],ok=0; 14 scanf("%d%d",&n,&a); 15 for(int i=0;i<n;i++) scanf("%d",num+i); 16 sort(num,num+n); 17 queue<pii> q; 18 q.push(pii(a,0)); 19 while(!q.empty()) 20 { 21 pii tem=q.front(); q.pop(); 22 int x=tem.first,t=tem.second; 23 for(int i=n-1;i>=0;i--) 24 { 25 if(num[i]<=x) 26 { 27 int y=x%num[i]; 28 if(y==0) 29 { 30 ok=1; 31 printf("%d\n",t+1); 32 while(!q.empty()) q.pop(); 33 break; 34 } 35 else q.push(pii(y,t+1)); 36 } 37 } 38 } 39 if(!ok) printf("-1\n"); 40 } 41 return 0; 42 }
待补。