递归练习1

感觉自己这方面很弱,都是看着题解做的orz..

fzu2038 Another Postman Problem(递归求解)

题意:n个点n-1条边组成无向连通图,求每个点到其他所有点的路径总和的和。

题解:每条边的访问次数为边两端点数乘积的两倍。递归遍历每个点的每条边即可。

 1 #include<cstdio>
 2 #include<vector>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=1e5+5;
 6 typedef long long ll;
 7 int n;
 8 ll sum;
 9 struct edge{
10     int v,c;
11     edge(int v,int c):v(v),c(c){}
12 };
13 vector<edge>g[N];
14 int vis[N];
15 ll dfs(int u){
16     vis[u]=1;
17     ll cnt=1;
18     for(int i=0;i<g[u].size();++i){
19         int v=g[u][i].v;
20         if(vis[v])continue;
21         ll t=dfs(v);
22         sum+=2*g[u][i].c*t*(n-t);
23         cnt+=t;
24     }
25     return cnt;
26 }
27 int main(){
28     int i,t,k,a,b,c;
29     scanf("%d",&t);
30     for(k=1;k<=t;++k){
31         scanf("%d",&n);
32         for(i=0;i<n;++i)g[i].clear();
33         memset(vis,0,sizeof(vis));
34         for(i=0;i<n-1;++i){
35             scanf("%d%d%d",&a,&b,&c);
36             g[a].push_back(edge(b,c));
37             g[b].push_back(edge(a,c));
38         }
39         sum=0;
40         dfs(0);
41         printf("Case %d: %I64d\n",k,sum);
42     }
43     return 0;
44 }
View Code

 

hdu5355 Cake(回溯)

题意:有n个尺寸大小分别为1,2,3..,n的蛋糕,问能否将之平均分成m份(不能再切割),能的话输出每份蛋糕数量及尺寸。

题解:无解情况有两种:

①所有蛋糕大小之和不能被m整除;

②每一份蛋糕大小之和小于蛋糕大小最大值n,转化即n<2m-1。

其他情况有解。一直用2m去减n,直至n小于40(不知道为什么是40(>_<))然后回溯法求40以内能划分的情况。

官方题解:

//img-blog.csdn.net/20150806185547668

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<vector>
 4 using namespace std;
 5 const int N=41;
 6 int a[N],id[N],n,m;
 7 vector<int>g[N];
 8 int dfs(int n,int k){
 9     if(n<1)return 1;
10     for(int i=1;i<=m;++i){
11         if(a[i]+n<=k){
12             id[n]=i;
13             a[i]+=n;
14             if(dfs(n-1,k))return 1;
15             id[n]=0;
16             a[i]-=n;
17         }
18     }
19     return 0;
20 }
21 void solve(int n){
22     int i,j;
23     for(i=1;i<=m;++i){
24         g[i].clear();
25         a[i]=id[i]=0;
26     }
27     while(n>40){
28         for(i=1;i<=m;++i) g[i].push_back(n--);
29         for(i=m;i>=1;i--) g[i].push_back(n--);
30     }
31     dfs(n,n*(n+1)/2/m);
32     for(i=1;i<=n;++i) g[id[i]].push_back(i);
33     for(i=1;i<=m;++i){
34         sort(g[i].begin(),g[i].end());
35         int len=g[i].size();
36         printf("%d",len);
37         for(j=0;j<len;++j) printf(" %d",g[i][j]);
38         printf("\n");
39     }
40 }
41 int main(){
42     int t;
43     scanf("%d",&t);
44     while(t--){
45         scanf("%d%d",&n,&m);
46         if((1LL*n*(n+1)/2)%m||n<2*m-1) printf("NO\n");
47         else{
48             printf("YES\n");
49             solve(n);
50         }
51     }
52     return 0;
53 }
View Code

hdu5225 Tom and permutation(回溯)

题意:给出一个n的排列,求所有字典序小于给定排列的逆序对数之和。

题解:预处理1~N所有全排列的逆序数和。枚举第k位。dfs,每次考虑当前位(之前的考虑过),如果与原序列对应数不同,后面的逆序对数可得,排列数为阶乘,然后求当前位对后面产生的逆序对数;否则,将后面数对逆序对数的影响留到后面的dfs中改。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 typedef long long ll;
 6 const int N=101;
 7 const int mod=1e9+7;
 8 ll s[N],num[N];//逆序数和,排列数
 9 int vis[N],a[N],n,ans;
10 ll dfs(int k,int f){
11     if(k>n) return 0;
12     if(f==0){
13         ans=(ans+s[n-k+1])%mod;
14         return num[n-k+1];
15     }
16     else{
17         ll cnt=0;
18         for(int i=1;i<=a[k];++i){
19             if(vis[i])continue;
20             vis[i]=1;
21             int s1=0,j;
22             for(j=1;j<i;++j)
23                 if(vis[j]==0) s1++;
24             ll t=dfs(k+1,i==a[k]?1:0);
25             vis[i]=0;
26             ans=(ans+t*s1%mod)%mod;
27             cnt=(cnt+t)%mod;
28         }
29         return cnt;
30     }
31 }
32 int main(){
33     int i;
34     s[1]=0;
35     num[1]=1;
36     for(i=2;i<N;++i){
37         s[i]=s[i-1]*i%mod+num[i-1]*((1LL*i*(i-1)/2)%mod)%mod;
38         num[i]=num[i-1]*i%mod;
39         //printf("%lld %lld\n",s[i],num[i]);
40     }
41     while(scanf("%d",&n)==1){
42         ans=0;
43         for(i=1;i<=n;++i) scanf("%d",&a[i]);
44         memset(vis,0,sizeof(vis));
45         dfs(1,1);
46         printf("%d\n",ans);
47     }
48     return 0;
49 }
View Code

 

hdu4911 Inversion (归并排序)

题意:给出一个序列,可以交换相邻位置的数不超过k次,求交换后最小逆序数。

题解:用归并方法求原序列逆序数cnt,在归并过程中计算每个小区间的逆序对数,进而计算出大区间的逆序对数。因为交换一次可以减少一个逆序对数。所以最后答案为max(0,cnt-k)。

注意,归并排序是稳定的排序,即相等的元素的顺序不会改变,这是它比快速排序优势之处。

 1 #include<cstdio>
 2 long long cnt,k;
 3 int n,a[100001],b[100001];
 4 void Merge(int l,int m,int r){
 5     int i=l,j=m+1,c=0;
 6     while(i<=m||j<=r){
 7         if(a[i]<=a[j]&&i<=m||j>r)
 8             b[c++]=a[i++];
 9         else{
10             b[c++]=a[j++];
11             cnt+=(m-i+1);
12         }
13     }
14     for(i=0;i<c;++i)
15         a[l+i]=b[i];
16 }
17 void merge_sort(int l,int r){
18     if(l<r){
19         int m=(l+r)/2;
20         merge_sort(l,m);
21         merge_sort(m+1,r);
22         Merge(l,m,r);
23     }
24 }
25 int main(){
26     while(scanf("%d%lld",&n,&k)==2){
27         for(int i=0;i<n;++i) scanf("%d",&a[i]);
28         cnt=0;
29         merge_sort(0,n-1);
30         if(k>=cnt)printf("0\n");
31         else printf("%lld\n",cnt-k);
32     }
33     return 0;
34 }
View Code

 

hdu5323 Solve this interesting problem(dfs)

题意:求最小的线段树的右端点,使得给定的区间[L,R]是某节点

题解:注意题目的数据,L/(R-L+1)<=2015,向上搜时,L不变,(R-L+1)翻倍,所以L/(R-L+1)每上一层变为原来的1/2,所以层数最多只有log22015=11,不难想到dfs暴力扩展。

从[L,R]向根部搜,根据(L+R)的奇偶性判断其父区间有四种可能:[L,2R-L]、[2(L-1)-R,R]、[L,2R-L+1]、[2(L-1)-R+1,R]。其中父节点为[L,2R-L]时,必须R>L,否则它右儿子为空,就矛盾了。

注意到无解条件:L<(R-L+1),因为左儿子不可能比右儿子表示的区间长度小。

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 typedef long long ll;
 5 const ll inf=0x3f3f3f3f;
 6 ll L,R,n;
 7 void dfs(ll l,ll r){
 8     if(l==0||r>=2*R){
 9         if(l==0&&n>r)n=r;
10         return;
11     }
12     int c=r-l+1;
13     if(l<c)return;
14     dfs(l-c,r);
15     dfs(l-c-1,r);
16     dfs(l,r+c);
17     if(c>1) dfs(l,r+c-1);
18 }
19 int main(){
20     while(scanf("%lld%lld",&L,&R)==2){
21         n=inf;
22         dfs(L,R);
23         printf("%lld\n",n==inf?-1:n);
24     }
25 }
View Code

 

posted @ 2016-08-07 15:28  GraceSkyer  阅读(251)  评论(0编辑  收藏  举报

~~~~~~ACM大牛语录,激励一下~~~~~~

为了世界的和平,为了女生的安全,我拼命做题,做题,做题!

用最短的时间,刷最多的题!

给我一滴泪,我就看到了你全部的海洋!

seize the hour, seize the day.

人生难免有无奈,幸福走远了,或是感叹幸福来迟了.其实我一直相信,无论手中的幸福是多么微不足道的感觉,我会把握住那每一分,每一秒,当幸福依旧像那百鸟般飞逝,终究无法掌握时,我会感谢它,曾经降临过!

A自己的题,让别人郁闷去吧

WA肠中过,AC心中留 TLE耳边过,AC特别牛

天然的悲苦和伤逝,过去有过,以后还会有

^*^一步一步往上爬^*^

AC就像练级,比赛就像PK. 练级不如PK好玩

其实,世上本没有ACM,AC的人多了,也便有了!

AC无止尽~ Seek you forever~

找呀找呀找水题,找到一个AC一个呀!

AC是检验程序的唯一标准。

真的猛士,敢于直面惨淡的人生,敢于正视淋漓的鲜血……