第二周 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 }
Aguin

 

学最大流的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 }
Aguin

 

在补多校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 }
Aguin

 

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 }
Aguin

 

背包问题里面有一类能不能达到的问题。(就是只有背包装满才是可行解)

以一般的求价值最大的背包为例。

初始化的时候把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 }
Aguin

 

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 }
Aguin

 

对于混合背包问题。

如果只有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 }
Aguin

 

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 }
Aguin

 

 

晚上打了个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 }
Aguin

 

待补。

posted @ 2015-07-26 12:23  Aguin  阅读(241)  评论(0编辑  收藏  举报