学习总结(大数运算,简单dp,线段树,并查集,最短路)

断断续续的搞ACM已经有一段时间,现在对已经学过的做个总结。

一.大数

对于爆long long 的数据,首先用字符传读入,然后按照运算规则一位一位的处理。

1.加法(O(n))

HDU1002 

http://acm.hdu.edu.cn/showproblem.php?pid=1002

#include<string.h>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long int
#define maxn 10000
void sum(char *ss1,char *ss2,char *ans)
{
int  i,s1[maxn],s2[maxn],s3[maxn],t=0,maxx;
maxx=max(strlen(ss1),strlen(ss2));
memset(s1,0,sizeof(s1));
memset(s2,0,sizeof(s2));
memset(s3,0,sizeof(s3));
for( i=strlen(ss1)-1;i>=0;i--) s1[t++]=ss1[i]-'0';
t=0;
for( i=strlen(ss2)-1;i>=0;i--) s2[t++]=ss2[i]-'0';
t=0;
while(t<maxx)
{
s3[t]=s3[t]+s1[t]+s2[t];
if(s3[t]>=10)
{
s3[t+1]=s3[t]/10;
s3[t]=s3[t]%10;
}
t++; 
}
if(s3[t]==0) t--;
i=0;
while(t>=0)
{
ans[i++]=s3[t]+'0';
t--; 
}
ans[i]='\0';
 } 
 
 int main()
 {
  char s1[maxn],s2[maxn],ans[maxn];
  int t;
  scanf("%d",&t);
  for(int i=1;i<=t;i++)
  {
if(i!=1) printf("\n");
  scanf("%s%s",s1,s2);
  sum(s1,s2,ans);
  printf("Case %d:\n",i);
  printf("%s + %s = %s\n",s1,s2,ans);
}
 }

2.乘法(n2)

http://www.oj.swust.edu.cn/problem/show/1019

void changchar (int s,char *ss)
{
int t=0,i=0;
char s1[maxn];
while(s>=1)
{
s1[t++]=s%10+'0';
s=s/10;
}
s1[t]=s+'0';
ss[t+1]='\0';
while(t>=0) 
{
ss[i++]=s1[t--];
}
}
void multiplication(char *ss1,char *ss2,char *ans)
{
int  i,j,s1[maxn],s2[maxn],s3[maxn],t=0,maxx;
maxx=max(strlen(ss1),strlen(ss2));
memset(s1,0,sizeof(s1)),memset(s2,0,sizeof(s2)),memset(s3,0,sizeof(s3));
for( i=strlen(ss1)-1;i>=0;i--)s1[t++]=ss1[i]-'0';
t=0;
for( i=strlen(ss2)-1;i>=0;i--) s2[t++]=ss2[i]-'0';
t=0;
for( i=0;i<strlen(ss1);i++)
{
if(s1[i])
{

for(j=0;j<strlen(ss2);j++)
{
int w=i+j;
s3[w]=s3[w]+s1[i]*s2[j];
if(s3[w]>=10) 
{
s3[w+2]+=s3[w]/100;
s3[w+1]+=s3[w]/10%10;
s3[w]=s3[w]%10;
}
}
}
}
t=i+j+5;
i=0;
while(t--) if(s3[t]!=0) break;
while(t>=0) ans[i++]=s3[t--]+'0';
ans[i]='\0';
 }
 int main()
 {
  char s1[maxn],s2[maxn],ans[maxn];
  while(scanf("%s%s",s1,s2)!=EOF)
  {
  multiplication(s2,s1,ans);
printf("%s\n",ans);
}
 
 }

三.dp入门

1.数字三角形

2.

3.背包九讲

a.01背包

问题:N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 C i 1 ,得到的
价值是 W i 。求解将哪些物品装入背包可使价值总和最大(要求恰好装满背包,那么在初始化时除了 F[0] 为 0 ,其它
F[1..V ] 均设为 −∞ ,)

解法:

转移方程:F[i,v] = max{F[i − 1,v],F[i − 1,v − C i ] + W i }

伪代码:

F[0..V ]←0
for i ← 1 to N
for v ← V to C i
F[v] ← max{F[v],F[v − C i ] + W i }

http://acm.hdu.edu.cn/showproblem.php?pid=2602

#include<string.h>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 100000
int ans[maxn],value[maxn],volume[maxn]; 
 int main()
 {
 
  int n,t,v;
  cin>>t;
  while(t--)
  {
  cin>>n>>v;
  memset(ans,0,sizeof(ans));
for(int i=0;i<n;i++) scanf("%d",&value[i]); 
  for(int i=0;i<n;i++) scanf("%d",&volume[i]); 
for(int i=0;i<n;i++)
{
for(int j=v;j>=volume[i];j--) 
ans[j]=max(ans[j],ans[j-volume[i]]+value[i]);
}
printf("%d\n",ans[v]);
}
return 0;
 }

b.完全背包

问题:有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。放入第 i 种物品
的费用是 C i ,价值是 W i 。求解:将哪些物品装入背包,可使这些物品的耗费的费用总
和不超过背包容量,且价值总和最大。

伪代码:

F[0..V ]←0
for i ← 1 to N
for v ← C i to V
F[v] ← max(F[v],F[v − C i ] + W i )

http://acm.hdu.edu.cn/showproblem.php?pid=1114

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 0x7fffff
int value[508],weight[508],ans[10050];
int main()
{

    int n; 
    scanf("%d",&n);
    while(n--)
    {
    int e,f,t;
        scanf("%d%d%d",&e,&f,&t);
for(int i=0;i<=f;i++)
{
ans[i]=inf;
}
        for(int i=0;i<t;i++)
        {
            scanf("%d%d",&value[i],&weight[i]);
        }
        ans[0]=0;
        for(int i=0;i<t;i++)
        {
        for(int j=weight[i];j<=f-e;j++) ans[j]=min(ans[j],ans[j-weight[i]]+value[i]);
}
      if(ans[f-e]==inf) printf("This is impossible.\n");
      else printf("The minimum amount of money in the piggy-bank is %d.\n",ans[f-e]);
        
     } 
}


c.多重背包

问题:有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 M i 件可用,每件耗费的
空间是 C i ,价值是 W i 。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超
过背包容量,且价值总和最大。


可以将多重背包转换为01背包,方法是:将第 i 种物品分成若干件 01 背包中的物品,其中每件物品有一个系
数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数分别为
1,2,2 2 ...2 k−1 ,M i − 2 k + 1 ,且 k 是满足 M i − 2 k + 1 > 0 (2k为2的k次方)的最大整数。

http://acm.hdu.edu.cn/showproblem.php?pid=2191

#include"stdio.h"
#include"iostream"
#include"algorithm"
#include"string.h"
#include"math.h"
#include"string"
using namespace std;
int lg(int x){
    int count=0,t=1;
    while(x-t+1>0){
        t=2*t;
        count++;
    }
    return --count;
}
int d[100009],a[100009], p1[100000],h1[100000];
int main()
{
    //freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m,i,j,p[105],h[105],c[105];
        memset(d,0,sizeof(d));
        memset(p1,0,sizeof(p1));
        memset(h1,0,sizeof(h1));
        scanf("%d%d",&n,&m);
        int l=0;
        for(i=0;i<m;i++){
            scanf("%d%d%d",&p[i],&h[i],&c[i]);
            int s=lg(c[i]);
            int w=c[i]-pow(2,s)+1;
            p1[l]=p[i]*w;
            h1[l++]=h[i]*w;
            for(j=0;j<s;j++){
                w=pow(2,j);
                p1[l]=p[i]*w;
                h1[l++]=h[i]*w;
            }                    
        }
            for(i=0;i<l;i++){
                for(j=n;j>=p1[i];j--){
                    if(d[j]<d[j-p1[i]]+h1[i]){
                        d[j]=d[j-p1[i]]+h1[i];
                    }
                }
            }
        
        printf("%d\n",d[n]);
    }
    
}

四.数据结构入门

1.并查集


2.线段树

线段树是一种数据结构,主要对于区间查询、修改进行优化。

a.线段树单点更新

 http://blog.csdn.net/longshanxiaoxuesheng/article/details/47373739

 http://blog.csdn.net/longshanxiaoxuesheng/article/details/47373707

b.区间更新

http://blog.csdn.net/longshanxiaoxuesheng/article/details/50555142

c.区间合并

http://blog.csdn.net/longshanxiaoxuesheng/article/details/50577467

3.RMQ

六.图论入门

1.dfs,bfs

dfs(深度优先搜索)主要在于对递归函数的理解,由一个点不断的深入直到遇到正解就打印出来

http://acm.hdu.edu.cn/showproblem.php?pid=1016(HDU1016)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream> 
#include<cmath>
using namespace std;
#define ll long long int
#define maxn 1000
int n,ans[maxn],vis[maxn],f;


bool isprime(int num)//素数判断
{
for(int i=2;i<=(int)sqrt(num);i++)
{
if(num%i==0) return 0;
}
return 1;
}
void dfs(int s,int t)
{
if(t==n)
{
if(isprime(s+1))
{
for(int i=0;i<n;i++)
{
if(i==0)
printf("%d",ans[i]) ;
else printf(" %d",ans[i]);
}
printf("\n");
}
return ;
}
  for(int i=1;i<=n;i++) 
  {
  if(vis[i]==0&&isprime(s+i))
  {
//printf("i=%d\n",i) ;
  vis[i]=1;
  ans[t]=i;
  dfs(i,t+1);
  vis[i]=0;
}
}
}
 
int main()
{
int cnt=1;
while(scanf("%d",&n)!=EOF)
{
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));
printf("Case %d:\n",cnt);
vis[1]=1;
ans[0]=1;
  dfs(1,1);
  printf("\n");
  cnt++;
}
}

bfs(宽度优先搜索)

http://acm.hdu.edu.cn/showproblem.php?pid=1548

解法:对于电梯的上下关系可以用一副图来表示。要求最小的按下次数,则可以从起点不断的往里一层一层的搜索。如果搜索到的话,他所属于的层数就是最小的按下次数。即用bfs。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream> 
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
#define ll long long int
#define maxn 1000
int G[maxn][maxn],vis[maxn]; 
int main()
{
int n,A,B,k,mark,f;//mark 代表层数,f是表示是否能到达B 
while(scanf("%d",&n))
{
if(n==0) break;
//初始化 
memset(vis,0,sizeof(vis));
memset(G,0,sizeof(G));
f=0; 
scanf("%d%d",&A,&B);
/*因为A,B登小于200,所以将mark赋予一个很大的值,取后面几位有效。这样就可以很好的将队列中层数标记和图中的点分开 */ 
mark=10000001;
  // 存图,在第i层可到i+k和i-k 
for(int i=1;i<=n;i++)
{
scanf("%d",&k);
if(i+k<=n) G[i][i+k]=1;
if(i-k>0)  G[i][i-k]=1;
}
//A==B时则不需要按下 
if(A==B)
{
printf("0\n");
continue;
}
// bfs 
queue<int>q;
q.push(A);
q.push(mark);
while(!q.empty())
{
int v=q.front();
q.pop();
if(v<=300) 
{
vis[v]=1;
for(int i=1;i<=n;i++)
{
if(G[v][i]==1&&vis[i]==0)
{
if(i==B)
{
printf("%d\n",mark%100000);
f=1;
break;
}
q.push(i);
}
}
if(f==1) break;
}
else  
{
if(!q.empty())//避免一直把mark加入队列,陷入死循环。
{
mark++; 
q.push(mark);
}
else break;
}
}
if(f==0) printf("-1\n");
}
}

2.最短路求法

a.对于存图,主要有邻接矩阵和邻接表两种方法。关于求最短路的方法主要有Dijkstra(仅适应边权为正的情况)、Bellman-Ford(适用用于边权为负的情况)、Floyd等算法。

http://blog.csdn.net/longshanxiaoxuesheng/article/details/50828248

http://blog.csdn.net/longshanxiaoxuesheng/article/details/49560411






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值