文章目录
1. 最短路
1.1 dijstra v
#include<stdio.h>
#include<string.h>
#define inf 0x3f3f3f3f
int e[110][110],book[110];
int n,m,dis[110];
void dijstra(int a)
{
int minn,u;
for(int i=1; i<=n; i++){
dis[i]=e[a][i];
book[i]=0;
}
for(int i=1; i<=n-1; i++)
{
minn=inf;
for(int j=1; j<=n; j++)
if(minn>dis[j]&&book[j]==0)
{
minn=dis[j];
u=j;
}
book[u]=1;
for(int v=1; v<=n; v++)
if(dis[v]>dis[u]+e[u][v])
dis[v]=dis[u]+e[u][v];
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++){
if(i==j) e[i][j]=0;
else e[i][j]=inf;
}
int t1,t2,t3;
for(int i=1; i<=m; i++){
scanf("%d%d%d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
dijstra(1);
for(int i=1; i<=n; i++)
printf("%d ",dis[i]);
return 0;
}
1.2 bellman
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define N 1010
int n,m;
int d[N];
struct node {
int a,b,w;
} edge[N];
int bellman(int x) {
for(int i=1; i<=n; i++)
d[i]=inf;
d[x]=0;
for(int i=1; i<n; i++) //对每个点进行松弛
for(int j=1; j<m; j++) {
if(d[edge[j].a]+edge[j].w<d[edge[j].b])
d[edge[j].b]=d[edge[j].a]+edge[j].w;
}
for(int j=1; j<=n; j++) //还能松弛,有负边
if(d[edge[j].a]+edge[j].w<d[edge[j].b])
return 0;
return 1;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].w);
int flag=bellman(1);//是否有负边权
printf("flag=%d\n",flag);
for(int i=1;i<=n;i++)
printf("%d ",d[i]);
return 0;
}
return 0;
}
1.3 SPFA
#include<stdio.h>
#include<string.h>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
#define mem(x,y) memset(x,y,sizeof(x))
#define inf 0x3f3f3f3f
#define N 30010
int head[N],dis[N],vis[N];
int n,m,i,k;
struct node
{
int v,w;
int next;
}edge[5*N];
void add(int u,int v,int w) //邻接表
{
edge[k].v=v;
edge[k].w=w;
edge[k].next=head[u];
head[u]=k++;
}
void SPFA() //栈优化
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
dis[i]=inf;
stack<int> M;
dis[1]=0;
vis[1]=1;
M.push(1);
while(!M.empty())
{
int u=M.top();
M.pop();
vis[u]=0;
for(i=head[u]; i!=-1; i=edge[i].next){
int v=edge[i].v; //u->v
if(dis[v]>dis[u]+edge[i].w){
dis[v]=dis[u]+edge[i].w;
if(!vis[v]){ //v不在栈,入栈
vis[v]=1;
M.push(v);
}
}
}
}
}
void spfa() //队列优化
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
dis[i]=inf;
queue<int> q;
q.push(1);
dis[1]=0; //以1开始
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(dis[v]>dis[u]+edge[i].w){
dis[v]=dis[u]+edge[i].w;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
int main()
{
int t1,t2,t3;
while(~scanf("%d%d",&n,&m)){
k=0;
memset(head,-1,sizeof(head));
for(i=1; i<=m; i++){
scanf("%d %d %d",&t1,&t2,&t3);
add(t1,t2,t3);
}
//SPFA(); //栈优化
spfa();
printf("%d\n",dis[n]);
}
return 0;
}
2.最长公共上升子序列(长度)
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[50005],sum[50005],dp[50005][4];
int main()
{
int i,j,n,m,t;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
scanf("%d",&m);
for(i=m;i<=n;i++)
for(j=3;j>=1;j--)
dp[i][j]=max(dp[i-1][j],dp[i-m][j-1]+sum[i]-sum[i-m]);
printf("%d\n",dp[n][3]);
}
return 0;
}
3.1区间素数打表
区间素数打表
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
bool prim[100010];
bool prims[100010];
void segment_sieve(LL a,LL b)
{
for(int i=0; (LL)i*i<=b; i++) prims[i]=1;
for(int i=0; i<=b-a; i++) prim[i]=1;
for(int i=2; (LL)i*i<=b; i++)
{
if(prims[i])
{
for(int j=2*i; (LL)j*j<=b; j+=i)
{
prims[j]=0;
}
for(LL j=max(2LL,(a+i-1)/i)*i; j<=b; j+=i)
{
prim[j-a]=0;
}
}
}
}
int main()
{
LL a,b;
int t,i,sum,cas=1;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&a,&b);
segment_sieve(a,b);
sum=0;
for(i=0; i<=b-a; i++)
{
if(prim[i])
sum++;
}
if(a==1)sum--; //特殊情况
printf("Case %d: %d\n",cas++,sum);
}
return 0;
}
3.2埃氏打表模板
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
#define N 1000001
int f[N],prim[N];
int k=0;
void find_prim()
{
k=0;
int c;
memset(f,0,sizeof(f));
f[0]=f[1]=1;
for(int i=2; i<=1000000; i++) //此处不要用N,用具体的数
{
c=i;
if(!f[i])
{
prim[k++]=c;
for(ll j=i<<1; j<=1000000; j+=i) //这里也是,还有要把N开的大点,防止这里越界
f[j]=1;
}
}
}
int main()
{
int i,j,n,m;
find_prim();
for( i=0;i<=100;i++)
printf("%d\n",prim[i]);
}
3.3欧拉打表模板
#include<stdio.h>
#include<string.h>
#define N 100000000
int p[N+5],prim[N+5];
void prime()
{
int i,j,k=0;
memset(p,0,sizeof(p));
p[0]=p[1]=1;
for(i=2;i<=N;i++)
{
if(!p[i])
prim[k++]=i;
for(j=0; j<k&&i*prim[j]<=N ;j++)
{
p[i*prim[j]]=1;
if(i%prim[j]==0)
break;
}
}
}
int main()
{
prime();
for(int i=0;i<=100;i++)
printf("%d\n",prim[i]);
}
4.KMP v
#include<stdio.h>
#include<string.h>
char a[1010],b[1010];
int p[1010],n,m;
void pre()
{
int j=0;
p[1]=0;
for(int i=1;i<m;i++){
while(j>0&&b[j+1]!=b[i+1]) j=p[j];
if(b[j+1]==b[i+1])j++;
p[i+1]=j;
}
}
void kmp()
{
int ans=0,j=0;
for(int i=0;i<n;i++){
while(j>0&&a[i+1]!=b[j+1]) j=p[j];
if(a[i+1]==b[j+1]) j++;
if(j==m){
//ans=i+1-m+1; //求模板串在母串中出现的位置
//printf("%d\n",ans);
//j=p[j];
ans++; //求模板串在母串里出现的次数
j=0;
}
}
printf("%d\n",ans);
}
int main()
{
while(~scanf("%s%s",a+1,b+1)){
m=strlen(b+1),n=strlen(a+1);
pre();
kmp();
}
return 0;
}
KMP例题
题解:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 1000000
#define M 100000
char a[N+10],b[M+10];
int p[M+10],n,m,ans;
void pre()
{
int j=0;p[1]=0;
for(int i=1;i<m;i++){
while(j>0&&b[j+1]!=b[i+1]) j=p[j];
if(b[j+1]==b[i+1]) j++;
p[i+1]=j;
}
}
int KMP()
{
int j=0;ans=0;
for(int i=0;i<n;i++){
while(j>0&&a[i+1]!=b[j+1]) j=p[j];
if(a[i+1]==b[j+1]) j++;
if(j==m){
ans++;
j=p[j];
}
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(p,0,sizeof(p));
scanf("%s%s",b+1,a+1);
n=strlen(a+1);
m=strlen(b+1);
pre();
printf("%d\n",KMP());
}
return 0;
}
5.快速幂
int quick(int a,int b,int mod)
{
int res=1;
while(b)
{
if(b&1) res=(res*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return res;
}
6.唯一分解定理
int river(ll a) //求a的正因子数,prim数组为已经打好表的素数
{
ll s=1,count=0,i=0;
while(a>=prim[i]&&i<k)
{
count=0;
if(a%prim[i]==0)
{
while(a%prim[i]==0)
{
a=a/prim[i];
count++;
}
}
s*=count+1; //
i++;
}
if(a>1)
s*=1+1; //最后剩下质因子,为一次幂。
return s;
}
6.2.求一个数的质数因子总数
//打表
int f[N+5];
void getsum()
{
memset(f,0,sizeof(f));
for(i=2; i<=N; i++)//i代表因子
{
if(f[i]==0) //素数,同理于埃氏筛法
for(int j=i; j<=N; j+=i)//i的倍数,从i开始,保证k*i
{
int k=j;
while(k%i==0)
{
p[j]++;
k/=i;
} //p[素数]一定会是1,只有它本身
}
}
for(int i=2;i<=N;i++)
p[i]=p[i]+p[i-1];
//p[2]=p[2]+p[1]=1+0=1,p[1]=0
}
7.拓扑排序
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,book[maxn][maxn];
struct node
{
int x,y;
}s[1010];
int main()
{
while(~scanf("%d%d",&n,&m))
{
int l=0,k,a,b,i,j;
memset(s,0,sizeof(s));
memset(book,0,sizeof(book));
for(i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
if(book[a][b]==0)//摆平重复的情况
{
book[a][b]=1; //有关系的标记一下
s[b].x++;
} //记录入度
}
for(i=0;i<n;i++)
{
for(j=1;j<=n;j++)
{
if(s[j].x==0)
{
k=j; //找到入度为零的点
break;
}
}
s[l++].y=k; //记录到数组中
s[k].x=-1; //入读标记为-1
for(j=1;j<=n;j++)
{
if(book[k][j]==1) //与当前点相连的点
s[j].x--; //入度--
}
}
for(i=0;i<l;i++)
{
if(i!=l-1)
printf("%d ",s[i].y);
else
printf("%d\n",s[i].y);
}
}
return 0;
}
8.欧拉函数
const int M=5000000;
const int N=5000010;
ll p[N];
void Euler()
{
for(int i=1;i<=M;i++)
p[i]=i;
for(int i=2;i<=M;i++)
{
if(p[i]==i)
{
for(int j=i;j<=M;j+=i)
p[j]=p[j]/i*(i-1);
}
}
}
9.1线段树基本操作
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 400010
int inf=99999999;
using namespace std;
long long mi[N],a[N];
long long p=1,q=1;
void buildmin(int k,int l,int r) //最小值建树
{
if(l==r)
{
mi[k]=a[l];
return;
}
int mid=(l+r)/2;
buildmin(k<<1,l,mid);
buildmin(k<<1|1,mid+1,r);
mi[k]=min(mi[k<<1],mi[k<<1|1]);//自下而上,当前K取左右儿子较小,**该位置的条件因题而异**
}
void buildsum(int k,int l,int r) //求和建树
{
if(l==r)
{
mi[k]=a[l];
return;
}
int mid=(l+r)/2;
buildsum(k<<1,l,mid);
buildsum(k<<1|1,mid+1,r);
mi[k]=mi[k<<1]+mi[k<<1|1]; //变化仅此而已
}
int query_min(int k,int l,int r,int x,int y) //简单的区间询问操作
{
if(r<x||l>y)return inf;
if(x<=l&&r<=y)return mi[k];
int mid=(l+r)/2;
return min(query_min(k<<1,l,mid,x,y),query_min(k<<1|1,mid+1,r,x,y)); //果然,最小值建树区间访问求最小值比较容易....
}
void change(int k,int l,int r,int x,int v) //点修改操作
{
if(l>x||r<x)return;
if(l==r&&l==x)
{
mi[k]=v;
return;
}
int mid=(l+r)/2;
change(k<<1,l,mid,x,v);
change(k<<1|1,mid+1,r,x,v);
mi[k]=min(mi[k<<1],mi[k<<1|1]); //感觉和建树bulid类似
}
9.2区间修改与区间查询 v
void pushdown(int k,int l,int r)
{
if(lazy[k]){
lazy[k*2+1]=lazy[k*2]=lazy[k];
sum[k*2]=lazy[k]*l;
sum[k*2+1]=lazy[k]*r;
lazy[k]=0;
}
}
void update(int k,int l,int r,int x,int y,int v)
{
if(x<=l&&r<=y){
sum[k]=v*(r-l+1);
lazy[k]=v;
return;
}
int mid=(l+r)/2;
pushdown(k,mid-l+1,r-mid);
if(x<=mid) update(k*2,l,mid,x,y,v);
if(mid<y) update(k*2+1,mid+1,r,x,y,v);
sum[k]=sum[k*2]+sum[k*2+1];
}
ll query(int k,int l,int r,int x,int y)
{
if(l>=x&&r<=y)return sum[k];
int mid=l+r>>1;
ll res=0;
pushdown(k,mid-l+1,r-mid);
if(x<=mid)res+=query(k<<1,l,mid,x,y);
if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
return res;
}
== 线段树例题==
题解
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 100000
#define ll long long
ll sum[4*N+10],lazy[4*N+10];
int a[N+10],n,m;
void build(int k,int l,int r)
{
if(l==r){
sum[k]=a[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
sum[k]=sum[k<<1]+sum[k<<1|1];
}
void add(int k,int l,int r,int v) //k,l,r,v
{
lazy[k]+=v;
sum[k]+=(ll)v*(r-l+1); //给相应区间加上一定值'+='
}
void pushdown(int k,int l,int r,int mid) //k,l,r
{
if(lazy[k]){
add(k<<1,l,mid,lazy[k]);
add(k<<1|1,mid+1,r,lazy[k]);
lazy[k]=0;
}
}
void modify(int k,int l,int r,int x,int y,int v)
{
if(l>=x&&r<=y) return add(k,l,r,v); //直接更新
int mid=(l+r)>>1;
pushdown(k,l,r,mid);
if(x<=mid) modify(k<<1,l,mid,x,y,v);
if(y>mid) modify(k<<1|1,mid+1,r,x,y,v);
sum[k]=sum[k<<1]+sum[k<<1|1]; //节点更新
}
ll query(int k,int l,int r,int x,int y)
{
ll res=0;
if(l>=x&&r<=y) return sum[k]; //返回值
int mid=(l+r)>>1;
pushdown(k,l,r,mid);
if(x<=mid) res+=query(k<<1,l,mid,x,y); //'+='
if(y>mid) res+=query(k<<1|1,mid+1,r,x,y);
return res;
}
int main()
{
char c;
memset(lazy,0,sizeof(lazy));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
getchar();
int x,y,z;
scanf("%c%d%d",&c,&x,&y);
if(c=='Q'){
ll ans=query(1,1,n,x,y);
printf("%lld\n",ans);
}
if(c=='C'){
scanf("%d",&z);
modify(1,1,n,x,y,z);
}
}
return 0;
}
10.已知两个矩形对角点坐标,求相交部分的矩形面积以及坐标
int xx1,yy1,xx2,yy2;//相交部分坐标
long long query(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)
{
ll a,b;
xx1=max(x1,x3);
xx2=min(x2,x4);
a=xx2-xx1;
if(a<0) return 0;
yy1=max(y1,y3);
yy2=min(y2,y4);
b=yy2-yy1;
if(b<0) return 0;
return a*b;//相交面积
}
11.Tarjian算法(求强联通分量)
简单讲就是用深搜所有点,先把能走到的点入栈,走遍每个点的所有出边
如果新边已经在栈中了,就更新当前点的low值。
此时,在递归返回上一层级时,在该点之前的节点也会更新其low值。
当该点无边可走,且当前dfn值与low值相同时,代表该点是一个强联通分量的根
弹出包括该节点以及该节点之前的所有点
道理其实很简单,能通过递归达到并入栈的点,代表了至少单方向是能走通的,当某一点的下一条可行边指向栈中时,则说明能构一个环,环上的任意两点都是联通的。故更新low值。
有的时候tarjian需要不止一次
/*清空栈与容器操作
for(int i=0;i<=n;i++)
edge[i].clear();
while(!s.empty()) s.pop();
*/
#include<stdio.h>
#include<stack>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 1010
vector<int>edge[N];
vector<int>a[N];//存放强联通分组元素
stack<int>s;
int low[N],dfn[N];
bool vis[N];
int k=0,l=0; //k:d节点访问次序,即时间戳
void tarjian(int u)
{
low[u]=dfn[u]=++k;
s.push(u);
vis[u]=true;
for(int i=0;i<edge[u].size();i++){
int v=edge[u][i];
if(!dfn[v]){
tarjian(edge[u][i]);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
++l;
while(1){
int x=s.top();
s.pop();
vis[x]=false;
a[l].push_back(x); //可无
if(u==x) break;
}
}
}
int main()
{
memset(vis,0,sizeof(vis));
int n,m,u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
edge[u].push_back(v);
//edge[a][.push_back(b) a与b相连
}
tarjian(1);
for(int i=1;i<=l;i++){
printf("Case:%d\n",i);
for(int j=0;j<a[i].size();j++)
printf("%d ",a[i][j]);
printf("\n");
}
return 0;
}
/*
7 11
1 2
2 3
2 5
2 4
3 5
3 7
7 5
5 6
6 7
4 1
4 5
*/
12.求两个圆相交部分面积
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#define pi acos(-1.0)
using namespace std;
const double fps=1e-8;
double same(double x1,double y1,double r1,double x2,double y2,double r2)
{
double d=sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
if(r1+r2<d+fps) return 0;
if(d<fabs(r1-r2)+fps){
double r=min(r1,r2);
return pi*r*r;
}
double x=(d*d+r1*r1-r2*r2)/(2.0*d);
double t1=acos(x/r1);
double t2=acos((d-x)/r2);
return r1*r1*t1+r2*r2*t2-d*r1*sin(t1);
}
例题:VJ链接
解法:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#define pi acos(-1.0)
using namespace std;
const double fps=1e-8;
double same(double x1,double y1,double r1,double x2,double y2,double r2)
{
double d=sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
if(r1+r2<d+fps) return 0;
if(d<fabs(r1-r2)+fps){
double r=min(r1,r2);
return pi*r*r;
}
double x=(d*d+r1*r1-r2*r2)/(2.0*d);
double t1=acos(x/r1);
double t2=acos((d-x)/r2);
return r1*r1*t1+r2*r2*t2-d*r1*sin(t1);
}
int main()
{
int t,cas=1;
double x1,x2,y1,y2,r1,r2;
scanf("%d",&t);
while(t--)
{
scanf("%lf%lf%lf%lf%lf%lf",&r1,&r2,&x1,&y1,&x2,&y2);
printf("Case #%d: ",cas++);
double s1=same(x1,y1,r2,x2,y2,r2);
double s2=same(x1,y1,r2,x2,y2,r1);
double s3=same(x1,y1,r1,x2,y2,r2);
double s4=same(x1,y1,r1,x2,y2,r1);
if(s4>0)
s4=s1-s2-s3+s4;
else
s4=s1-s2-s3;
printf("%.6f\n",s4);
}
return 0;
}
13.大数相加
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define mem(x,y) memset(x,y,sizeof(x))
#define N 1010
using namespace std;
char a[N],b[N];
int c[2*N];
void fan(char a[],int l)
{
for(int i=0;i<l;i++) a[i]-='0';
for(int i=0;i<l/2;i++)
swap(a[i],a[l-1-i]);
}
int main()
{
int t,l1,l2,L,i;
scanf("%d",&t);
while(t--){
mem(a,0),mem(b,0),mem(c,0);
scanf("%s%s",a,b);
l1=strlen(a);
l2=strlen(b);
L=max(l1,l2)+1;
fan(a,l1),fan(b,l2);
for(i=0;i<L+1;i++){
c[i]+=a[i]+b[i];
if(c[i]>=10){
c[i]-=10;
c[i+1]=1;
}
}
while(c[i]==0) i--;
for(i;i>=0;i--)
printf("%d",c[i]);
printf("\n");
}
return 0;
}
14.树状数组(一维)
const int N=MAXN;
int c[N];
int n,v;
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int y) //第x个数加y
{
while(x<=n){
c[x]+=y;
x+=lowbit(x);
}
}
int sum(int x) //1~x个数的和
{
int ans=0;
while(x){
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&v);
update(i,v); //更新输入
}
}
15 字典树
int ch[N][26],bo[N]; //26取决于字符种类数
int n,tot;
void insert(char *s)
{
int u=1,l=strlen(s);
for(int i=0; i<l; i++){
int c=s[i]-'a';
if(!ch[u][c])
ch[u][c]=++tot;
u=ch[u][c];
bo[u]=1;
}
}
int find(char *s)
{
int u=1,l=strlen(s);
for(int i=0; i<l; i++){
int c=s[i]-'a';
if(!ch[u][c]) return 0;
u=ch[u][c];
}
return bo[u];
}
16 网络流最大流
#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;
#define mem(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
int vis[20],pre[20];
int mp[20][20];
int n,m,s,e; //star,end
queue<int>dl;
bool bfs()
{
while(!dl.empty())dl.pop();
mem(vis,0),mem(pre,0);
vis[s]=1;
dl.push(s);
int a;
while(!dl.empty()){
a=dl.front();
dl.pop();
if(a==e)return true;
for(int i=2; i<=n; i++){
if(!vis[i]&&mp[a][i]){
dl.push(i);
vis[i]=1;
pre[i]=a;
}
}
}
return false;
}
int maxflow()
{
int ans=0;
while(1){
if(!bfs())return ans;
int a=e,temp=INF;
while(a!=s){
temp=min(temp,mp[pre[a]][a]);
a=pre[a];
}
a=e;
while(a!=s){
mp[pre[a]][a]-=temp;
mp[a][pre[a]]+=temp;
a=pre[a];
}
ans+=temp;
}
}
int main()
{
int t,cas=1;
scanf("%d",&t);
while(t--){
mem(mp,0);
int t1,t2,t3;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d%d%d",&t1,&t2,&t3);
mp[t1][t2]+=t3;
}
s=1,e=n;
printf("Case %d: %d\n",cas++,maxflow());
}
return 0;
}
例题:Flow Problem
17 二分图匹配
简单的二分图匹配,有男女分别nm人,每个女孩有自己的匹配意向,求最多的匹配对数
大致过程:
1 男孩 a 匹配女孩1
2 男孩 b 匹配女孩2
3 男孩 c 恰好遍历到女孩1可匹配
然后让a匹配女孩 2 (假设a和女孩2可匹配)
接着 b 匹配女孩3 (假设b和女孩3可匹配)
此时,通过调换给c腾出来一位配偶,实现了匹配数的增加
这个过程在算法中用递归实现,码量不大
例题:过山车
#include<stdio.h>
#include<string.h>
#define mem(x) memset(x,0,sizeof(x))
#define N 510
int b[N],book[N];
int dis[N][N];
int t,n,m;
bool search(int x)
{
for(int i=1;i<=n;i++)//找每个人可以匹配人
{
if(dis[x][i]&&!book[i])//判段联系和在之前递归过程中i有没有被匹配
{
book[i]=1;//在找x点的所有地柜中,把i强制占用,即假设x连i
if(b[i]==0||search(b[i]))//i未曾匹配过或者i的原配主可以调换配偶
{
b[i]=x;//可匹配
return true;
}
}
}
return false;
}
int main()
{
while(~scanf("%d",&t)&&t){
scanf("%d%d",&m,&n);
int x,y;
mem(dis),mem(b);
for(int i=0;i<t;i++){
scanf("%d%d",&x,&y);
dis[x][y]=1;
}
int ans=0;
for(int i=1;i<=m;i++){
mem(book); //每次都需要清空
if(search(i))ans++;
}
printf("%d\n",ans);
}
return 0;
}
18 并查集
#icnlude<stdio.h>
#include<string.h>
#defien N 1010
int f[N];
int getf(int v)
{
if(f[v]==v) return v;
return f[v]=getf(f[v]);
}
void merge(int x,int y)
{
int t1=getf(x);
int t2=getf(y);
if(t1!=t2) f[t2]=t1;
return;
}
19 判断线段相交
快速排斥实验,跨立实验
例题:hdu-2150
struct Node
{
double x,y;
};
Node node[50][110];
/*快速排斥实验 跨立实验*/
double cross(Node A, Node B, Node C) //AB.AC
{
return(B.x-A.x)*(C.y-A.y)-(C.x-A.x)*(B.y-A.y);
}
//判段AB和CD是否相交,相交返回 真
bool intersect(Node A, Node B, Node C, Node D)
{
if(min(A.y,B.y)<=max(C.y,D.y)&&
min(C.y,D.y)<=max(A.y,B.y)&&
min(A.x,B.x)<=max(C.x,D.x)&&
min(C.x,D.x)<=max(A.x,B.x)&&
cross(A,B,C)*cross(A,B,D)<0&&
cross(C,D,A)*cross(C,D,B)<0)
return true;
return false;
}
20.数组模拟邻接表与遍历所有边
邻接表存图与遍历每条边
/**
n个点,m条边
邻接表存所有边,并遍历
模板
sample input:
4 4 (n,m)
1 4
4 3
1 2
2 4
*/
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define mem(x,y) memset(x,y,sizeof(x))
#define N 10010
int first[N],next[N];
int u[N],v[N],w[N];
int n,m;
int main()
{
mem(first,-1);
mem(next,-1);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u[i],&v[i]);
next[i]=first[u[i]]; //核心
first[u[i]]=i; //代码
}
for(int i=1;i<=n;i++){
printf("\ni=%d\n",i);
//遍历某个点的所有边
for(int k=first[i];k!=-1;k=next[k])
printf("%d %d\n",u[k],v[k]);
printf("\n");
}
return 0;
}
21.最小生成树
克鲁斯卡尔,加边求最小生成树
n个点,m条边,求使得n个连起来的最短长度和
/**
Kruskal
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 3000
int n,m,sum,ans;
int f[N];
struct node
{
int a,b,c;
}e[N];
bool cmp(node x,node y)
{
return x.c<y.c;
}
int getf(int x)
{
if(f[x]==x) return x;
return f[x]=getf(f[x]);
}
void merge(int x)
{
int t1=getf(e[x].a);
int t2=getf(e[x].b);
if(t1!=t2){
f[t2]=t1; //合并树
sum+=e[x].c; //求最短长度和
ans++;
}
}
int main()
{
while(~scanf("%d%d",&n,&m)){
sum=0,ans=0; //最短长度,变数
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
sort(e+1,e+1+m,cmp); //从大到小排序
for(int i=1;i<=m;i++){ //从小边开始加入
merge(i); //合并边
if(ans==n-1) break;
}
printf("%d\n",sum);
}
return 0;
}
22 GCD
int gcd(int a,int b)
{
if(a%b==0) return a;
return gcd(b,a%b);
}
22.1 EX_GCD
求解二元方程的解,最小整数解,通解
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int x,y,g;
void exgcd(int a,int b) {
if(b==0) {
x=1,y=0,g=a;
return;
}
exgcd(b,a%b);
int t=x; //x=y y=x-a/b*y
x=y;
y=t-a/b*y;
}
int main() {
int a,b,c,x0,y0;
while(~scanf("%d%d%d",&a,&b,&c)) {
exgcd(a,b); //求解 ax+by=d 的解
if(c%g!=0) {
printf("无解\n");
continue;
}
x0=x*c/g; //特解:x=x0*c/g
y0=y*c/g; //y=y0*c/g
printf("x0=%d y0=%d\n",x0,y0);
int xmin,temp=b/g;
xmin=(x0%temp+temp)%temp; //xmin=(x%b/g+b/g)%b/g
printf("最小整数解:%d \n",xmin);
printf("通解:\n");
for(int i=0; i<=15; i++) {
int nx=x0+i*b/g;
int ny=y0-i*a/g;
printf("nx=%d ny=%d\n",nx,ny);
}
}
return 0;
}
23 1~n约数打表
#include<vector>
using namespace std;
#define N 1010
vector<int> f[N];
void fator_form(int n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n/i;j++) //1~n中以i为约数的数为i,2i,3i...(n/i)*i
f[i*j].push_back(i);
for(int i=1;i<=n;i++){
printf("i=%d ",i);
for(int j=0;j<f[i].size();j++)
printf("%d ",f[i][j]);
printf("\n");
}
return;
}
24 大数求组合数Lucas定理
给出比较大的一对数n,m,求C(n,m)%p
例题:
LibreOJ - 10228
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
ll n,m,p;
/**
大数组合数
*/
ll quick(ll a,ll b,ll p)
{
ll res=1;
while(b){
if(b&1) res=(res*a)%p;
a=a*a%p;
b>>=1;
}
return res;
}
ll C(ll x,ll y,ll p)
{
if(y==0) return 1;
ll a=1,b=1;
for(ll i=1;i<=y;i++){
a=(a*(x-i+1))%p; //n*(n-1)....(n-m+1)
b=(b*i)%p; //1*2*3.....m
} //(a/b)%p=a%p*(b^(p-2))%p %p
return a*quick(b,p-2,p)%p; //费马小定理优化
}
ll Lucas(ll x,ll y,ll p)
{
if(!y) return 1;
return (C(x%p,y%p,p)*Lucas(x/p,y/p,p))%p; //Lucas定理
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%lld%lld%lld",&n,&m,&p);
ll ss=Lucas(n,m,p);
printf("%lld\n",ss);
}
return 0;
}
25 欧拉函数模板
1试除法
int euler(int n)
{
int res=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
res=res/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) res=res/n*(n-1);
return res;
}
2.素数筛
#define N 1000000
int e[N+10];
void euler_form()
{
memset(m,0,sizeof(m));
e[1]=1;
for(int i=2;i<=N;i++)
if(!e[i]) //i每次筛选素数
for(int j=i;j<=N;j+=i){
e[j]=j;
e[j]=e[j]/i*(i-1);//i即j的素因子
}
return;
}
26 进制转换
十进制转R进制
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 10010
int a[1010];
int main()
{
char c;
int n,r;
while(~scanf("%d%d",&n,&r)){
int k=0,t;
while(n){
t=n%r;
a[k++]=t;
n/=r;
}
printf("转化为%d进制:\n",r);
for(int i=k-1;i>=0;i--){
if(a[i]>=10)
printf("%c",a[i]-10+'A');
else
printf("%d",a[i]);
}
printf("\n");
printf("\n再转换为十进制:\n");
int old=0;
for(int i=0;i<k;i++)
old+=a[i]*pow(r,i);
printf("old=%d\n",old);
}
return 0;
}
27 模拟数字相除(大数除以小数)
在此大于小指代数据位数的多少
要求:
输出a/b结果以及余数,(a的位数<=1000,可自拟)
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 1010 //按需改变
char a[N];
/**
大数 除以 "小"数 输出 值与余数
输入数据默认a大于b
*/
int main() {
int k,s,b;
while(~scanf("%s%d",a,&b)) {
int l=strlen(a);
for(int i=0; i<l; i++)
a[i]-='0';
k=0,s=0;
while(s<b)
s=s*10+a[k++];
while(1) { //模拟除法
if(s<b) printf("0");
else printf("%d",s/b);
s=s%b;
if(k==l) {
if(s) printf(" r....%d",s);
break;
}
s=s*10+a[k++];
}
printf("\n");
memset(a,0,sizeof(a));
}
return 0;
}
28 归并排序(求逆序数对)
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100100
int f[N],b[N];
ll ans=0;
void mergearray(int a[],int first,int mid,int last,int temp[]) {
int i=first,j=mid+1,k=0;
int m=mid,n=last;
while (i<=m&&j<=n) {
if(a[i]<=a[j])
temp[k++]=a[i++];
else {
temp[k++]=a[j++];
//ans+=mid-i+1; //a[i]有序,i~mid之间都是j的逆序数
}
}
while(i<=m)
temp[k++]=a[i++];
while(j<=n)
temp[k++]=a[j++];
for (i=0; i<k; i++)
a[first+i]=temp[i];
}
void mergesort(int a[], int first, int last, int temp[]) {
if (first<last) {
int mid = (first + last) / 2;
mergesort(a, first, mid, temp);//左边有序
mergesort(a, mid + 1, last, temp); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=0; i<n; i++)
scanf("%d",&f[i]);
mergesort(f,0,n-1,b);
//printf("%lld\n",ans);
return 0;
}