zoukankan      html  css  js  c++  java
  • 递归练习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
    ",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以内能划分的情况。

    官方题解:

    http://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("
    ");
    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
    ");
    47         else{
    48             printf("YES
    ");
    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
    ",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
    ",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
    ");
    31         else printf("%lld
    ",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==inf?-1:n);
    24     }
    25 }
    View Code
  • 相关阅读:
    docker 服务器安装harbor
    docker win10 推送镜像问题
    docker win10 基本指令
    docker、docker-compose安装,卸载
    go 名词备注
    go 结构开发规范
    Java基础--day14
    Java基础--day12
    Java基础--day11
    算法笔记--数据结构--树与二叉树
  • 原文地址:https://www.cnblogs.com/GraceSkyer/p/5746227.html
Copyright © 2011-2022 走看看