断断续续的搞ACM已经有一段时间,现在对已经学过的做个总结。
一.大数
对于爆long long 的数据,首先用字符传读入,然后按照运算规则一位一位的处理。
1.加法(O(n))
HDU1002
http://acm.hdu.edu.cn/showproblem.php?pid=1002
#include<string.h>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