1、最小生成树:v
+ v + value布线问题.................................................................2
4、输出最小生成树个边权值累加和(矩阵类型).....................................................5
5、求出最小生成树中边的最大值(矩阵类型)........................................................6
6、prim算法变形之引水工程(矩阵型)完全矩阵...................................................8
7、Kruskal(克鲁斯卡尔)(不是完全边,v+v+value)..........................................9
8、还差多少对v+v才能达到最小生成树................................................................11
9、先判断是否畅通,再用最小生成树求最小花费..................................................12
12、次小生成树(v+v+value型最小花费方案个数).............................................16
13、判断最小生成树是否唯一................................................................................19
13、最优比率生成树
(最优比例生成树)................................................................21
14、判断边与一个图最小生成shu的关系.............................................................23
15、hdu
3371 最小生成树(有重边)................................................................25
1、两列相同数的最大匹配数(月老的难题).........................................................26
1、士兵杀敌<1>(树状数组模板)........................................................................28
2、士兵杀敌<2>(树状数组模板)......................................................................29
3、士兵杀敌(三)(rmq求区间最大最小值之差)..............................................30
四、数组A[1..n]中超过半数的元素都相同时,该数组被称为含有主元素。...................32
2、快速排序一般版本(可求第K大元素)............................................................33
5、RMQ模板
,返回区间最值 、 最值的下标...................................................36
3、公交车最短路径(公交站是英文名字).............................................................42
4、单源最短路Dijkstra算法。只不过起点给你多个,然后求可以到达的目的地中路径最短的一条 45
5、飘谊想知道他走完观光道路并回到源点的最短路径...........................................47
6、给多个坐标,求最短路径的最大边....................................................................48
8、一棵树上的任意两点的距离..............................................................................52
10、aij矩阵类型的单源最短路径并且输出最短权值和最短路径.............................56
11、邻接矩阵的单源最短路径................................................................................58
2、现在要求从1点到n点在从n点返回1点的最短路..........................................63
3、点是否在凸多边形内或边上..............................................................................69
4、点在任意多边形内或边上..................................................................................69
4、求最长的公共子串,要注意的是字典序最小......................................................73
5、kmp算法模板(子串个数).............................................................................74
6、求S中最长回文串的长度.................................................................................75
9、求一个字符串的最长递增子序列的长度.............................................................81
一、最小生成树
1、最小生成树:v
+ v + value布线问题
布线问题:由于安全问题,只能选择一个楼连接到外界供电设备。
/*
1
v e
4 6(4个顶点,正好六个边)
1 2 10
2 3 10
3 1 10
1 4 1
2 4 1
3 4 1
1 3 5 6(连接到外界供电设备所需要的费用)
*/
#include<iostream>
using namespace std;
#define MAX 505
#define MAXCOST 0x7fffffff
int graph[MAX][MAX];
int prim(int graph[][MAX], int n)
{
int lowcost[MAX];
int mst[MAX];
int i, j, min, minid, sum =0;
for (i = 2; i <= n; i++)
{
lowcost[i] = graph[1][i];
mst[i] = 1;
}
mst[1] = 0;
for (i = 2; i <= n; i++)
{
min = MAXCOST;
minid = 0;
for (j = 2; j <= n;j++)
{
if (lowcost[j]< min && lowcost[j] != 0)
{
min =lowcost[j];
minid =j;
}
}
//cout <<"V" << mst[minid] << "-V" << minid<< "=" << min << endl;
sum += min;
lowcost[minid] = 0;
for (j = 2; j <= n;j++)
{
if(graph[minid][j] < lowcost[j])
{
lowcost[j]= graph[minid][j];
mst[j] =minid;
}
}
}
return sum;
}
int main()
{
int i, j, k, m, n;
int x, y, cost;
int zushu;
cin>>zushu;
while(zushu--)
{
cin >> m >> n;//m=顶点的个数,n=边的个数
for (i = 1; i <= m;i++)
{
for (j = 1; j<= m; j++)
{
graph[i][j]= MAXCOST;
}
}
for (k = 1; k <= n;k++)
{
cin >> i>> j >> cost;
graph[i][j] =cost;
graph[j][i] =cost;
}
cost = prim(graph, m);
int min_v=MAXCOST;
for(inti=1;i<=m;++i)
{
int t;
cin>>t;
if(t<min_v)
min_v=t;
}
cout <<cost+min_v<< endl;
}
return 0;
}
3、prim算法之矩阵类型
/*
1(组数)
3(3*3的矩阵)
0 990 692
990 0 179
692 179 0
//一个有n个顶点的无向图最多几条边
n(n-1)/2
//具有n个顶点的无向图,至少应有多少n-1条边才能确保是一个连通图
*/
#include "stdio.h"
#include "string.h"
#define N 500
#define INT 10000
bool vis[N];
int dis[N];
int a[N][N];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
int i,j,temp,k;
memset(vis,0,sizeof(vis));
for(i=1;i<=n;++i){
for(j=1;j<=n;++j){
scanf("%d",&a[i][j]);
}
}
for(i=1;i<=n;++i){
dis[i]=INT;
}
dis[1]=0;
for(i=1;i<=n;++i){
temp=INT;
k=0;
for(j=1;j<=n;++j){
if(!vis[j]&&dis[j]<temp){
temp=dis[j];
k=j;
}
}
vis[k]=1;
for(j=1;j<=n;++j){
if(!vis[j]&&dis[j]>a[k][j]){
dis[j]=a[k][j];
}
}
}
int max=0;
for(i=1;i<=n;++i){
if(max<dis[i])
max=dis[i];
}
printf("%d\n",max);
}
return 0;
}
4、输出最小生成树个边权值累加和(矩阵类型)
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
#include <stdio.h>
#include <string.h>
#define MaxInt 0x3f3f3f3f
#define N 110
int map[N][N],low[N],visited[N];
int n;
int prim()
{
int i,j,pos,min,result=0;
memset(visited,0,sizeof(visited));
visited[1]=1;pos=1;
for(i=1;i<=n;i++)
if(i!=pos)low[i]=map[pos][i];
for(i=1;i<n;i++)
{
min=MaxInt;
for(j=1;j<=n;j++)
if(visited[j]==0&&min>low[j])
{
min=low[j];pos=j;
}
result+=min;
visited[pos]=1;
//更新权值
for(j=1;j<=n;j++)
if(visited[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return result;
}
int main()
{
int i,v,j,ans;
while(scanf("%d",&n)!=EOF)
{
memset(map,MaxInt,sizeof(map));
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
scanf("%d",&v);
map[i][j]=map[i][j]=v;
}
ans=prim();
printf("%d\n",ans);
}
return 0;
}
5、求出最小生成树中边的最大值(矩阵类型)
1
3
0 990 692
990 0 179
692 179 0
692
#include<stdio.h>
#define MAX 505
#define inf 999999
int c[MAX][MAX];
int n;
void prim()
{
int lowcost[MAX ];
int closest[MAX ];
bool s[MAX ];
s[1]=true;
for(int i=2;i<=n;i++)
{
lowcost[i]=c[1][i];
closest[i]=1;
s[i]=false;
}
for(int i=1;i<=n;i++)
{
int min=inf;
int j=i;
for(int k=2;k<=n;k++)
if((lowcost[k]<min)&&(!s[k]))
{
min=lowcost[k];
j=k;
}
//cout<<j<<" "<<closet[j]<<endl;输出最小生成树的路径。
s[j]=true;
for(int k=2;k<=n;k++)
{
if((c[j][k]<lowcost[k])&&(!s[k]))
{
lowcost[k]=c[j][k];
closest[k]=j;
}
}
}
//最小生成树的边值已经放大lowcost数组中了。遍历一下就可以得到最大最小值。
int result=-1;
for(int i=2;i<=n;i++)
{
if(result<lowcost[i])
result=lowcost[i];
}
printf("%d\n",result);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&c[i][j]);
}
}
prim();
}
return 0;
}
6、prim算法变形之引水工程(矩阵型)完全矩阵
/*1
5
0 5 4 4 3 6
5 0 2 2 2 2
420
3 3 3
423
0 4 5
323
4 0 1
523
5 1 0*/
#include <stdio.h>
#include <string.h>
#define MaxInt 0x3f3f3f3f
#define N 303
int map[N][N],low[N],visited[N];
int n;
int prim()
{
int i,j,pos,min,result=0;
memset(visited,0,sizeof(visited));
visited[1]=1;pos=1;
for(i=1;i<=n;i++)
if(i!=pos)low[i]=map[pos][i];
for(i=1;i<n;i++)
{
min=MaxInt;
for(j=1;j<=n;j++)
if(visited[j]==0&&min>low[j])
{
min=low[j];
pos=j;
}
result+=min;
visited[pos]=1;
for(j=1;j<=n;j++)
if(visited[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return result;
}
int main()
{
int i,v,j,ans,zushu;
scanf("%d",&zushu);
int a[303];
while(zushu--)
{
scanf("%d",&n);
memset(map,MaxInt,sizeof(map));
map[1][1]=0;
for(i=2;i<=n+1;++i)
{
scanf("%d",&map[i][1]);
map[1][i]=map[i][1];
}
getchar();
for(i=2;i<=n+1;i++)
for(j=2;j<=n+1;j++)
{
scanf("%d",&v);
map[i][j]=map[i][j]=v;
}
n=n+1;
ans=prim();
printf("%d\n",ans);
}
return 0;
}
7、Kruskal(克鲁斯卡尔)(不是完全边,v+v+value)
/*
但不一定有直接的公路相连,只要能间接通过公路可达即可
村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。
对每个测试用例,在1行里输出全省畅通需要的最低成本。
若统计数据不足以保证畅通,则输出“?”。
Sample Input
3 3
1 2 1
1 3 2
2 3 4
1(e) 3(v)
2 3 2
0 100
Sample Output
3
?
*/
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 100
int father[MAX], son[MAX];
int v, l;
typedef struct Kruskal //存储边的信息
{
int a;
int b;
int value;
};
bool cmp(const Kruskal & a, const Kruskal & b)
{
return a.value < b.value;
}
int unionsearch(int x) //查找根结点+路径压缩
{
return x == father[x] ? x :unionsearch(father[x]);
}
bool join(int x, int y) //合并
{
int root1, root2;
root1 = unionsearch(x);
root2 = unionsearch(y);
if(root1 == root2) //为环
return false;
else if(son[root1] >=son[root2])
{
father[root2] =root1;
son[root1] +=son[root2];
}
else
{
father[root1] =root2;
son[root2] +=son[root1];
}
return true;
}
int main()
{
int ltotal, sum, flag;
Kruskal edge[MAX];
//scanf("%d",&ncase);
while(scanf("%d%d",&l, &v),l)
{
ltotal = 0, sum = 0,flag = 0;
for(int i = 1; i <=v; ++i) //初始化
{
father[i] = i;
son[i] = 1;
}
for(int i = 1; i <=l ; ++i)
{
scanf("%d%d%d",&edge[i].a, &edge[i].b, &edge[i].value);
}
sort(edge + 1, edge +1 + l, cmp); //按权值由小到大排序
int count=0;
for(int i = 1; i <=l; ++i)
{
if(join(edge[i].a,edge[i].b))
{
ltotal++;//边数加1
sum +=edge[i].value; //记录权值之和
//cout<<edge[i].a<<"->"<<edge[i].b<<endl;
}
if(ltotal == v- 1) //最小生成树条件:边数=顶点数-1
{
flag =1;
break;
}
count++;
}
//printf("count%d\n", count);
if(flag)
printf("%d\n",sum);
else
printf("?\n");
}
return 0;
}
8、还差多少对v+v才能达到最小生成树
/*
目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,
只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
v e
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0
输出最少还需要建设的道路数目。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int pre[1010];
int unionsearch(int root) //查找父节点+路径压缩(非递归)
{
int son, tmp;
son = root;
while(root != pre[root])
root = pre[root];
while(son != root)
{
tmp = pre[son];
pre[son] = root;
son = tmp;
}
return root;
}
/*int unionsearch(int root) //查找父节点+路径压缩(递归)
{
return root == pre[root] ?root : unionsearch(pre[root]);
}*/
int main()
{
int num, road, total, i, start,end, root1, root2;
while(scanf("%d%d",&num, &road) && num)
{
total = num - 1;
for(i = 1; i <= num;++i)
pre[i] = i;
while(road--)
{
scanf("%d%d",&start, &end);
root1 =unionsearch(start);
root2 =unionsearch(end);
if(root1 != root2)
{
pre[root1] = root2;
total--;
}
}
printf("%d\n",total);
}
return 0;
}
9、先判断是否畅通,再用最小生成树求最小花费
/*
对每个测试用例,在 1行里输出全省畅通需要的最低成本。
若统计数据不足以保证畅通,则输出 No solution。
2
e v
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
样例输出
3
No solution
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
//判断是否畅通
int pre[1010];
int unionsearch(int root) //查找父节点+路径压缩(非递归)
{
int son, tmp;
son = root;
while(root != pre[root])
root = pre[root];
while(son != root)
{
tmp = pre[son];
pre[son] = root;
son = tmp;
}
return root;
}
//最小生成树
#define MAX 505
#define MAXCOST 0x7fffffff
int graph[MAX][MAX];
int prim(int graph[][MAX], int n)
{
int lowcost[MAX];
int mst[MAX];
int i, j, min, minid, sum =0;
for (i = 2; i <= n; i++)
{
lowcost[i] =graph[1][i];
mst[i] = 1;
}
mst[1] = 0;
for (i = 2; i <= n; i++)
{
min = MAXCOST;
minid = 0;
for (j = 2; j <= n;j++)
{
if (lowcost[j]< min && lowcost[j] != 0)
{
min =lowcost[j];
minid =j;
}
}
//cout <<"V" << mst[minid] << "-V" << minid<< "=" << min << endl;
sum += min;
lowcost[minid] = 0;
for (j = 2; j <= n;j++)
{
if(graph[minid][j] < lowcost[j])
{
lowcost[j]= graph[minid][j];
mst[j] =minid;
}
}
}
return sum;
}
int main()
{
int num, road, total, start,end, root1, root2;
int i, j, k, m, n;
int x, y, cost;
int zushu;
cin>>zushu;
while(zushu--)
{
cin >> n >> m ;//m=顶点的个数,n=边的个数
num=m,road=n;
for (i = 1; i <= m;i++)
{
for (j = 1; j<= m; j++)
{
graph[i][j]= MAXCOST;
}
}
//------------判断是否畅通
//num顶点,road道路
total = num - 1;
for(i = 1; i <= num;++i)
pre[i] = i;
for (k = 1; k <= n;k++)
{
cin >> i>> j >> cost;
graph[i][j] =cost;
graph[j][i] =cost;
start=i;
end=j;
root1 =unionsearch(start);
root2 =unionsearch(end);
if(root1 != root2)
{
pre[root1] = root2;
total--;
}
}
//cout<<total<<endl;
if(total<1)
{
cost = prim(graph, m);
printf("%d\n",cost);
}
else{
printf("No solution\n");
}
}
return 0;
}
10、dfs欧拉回路
/*
2
v e
4 3
1 2
1 3
1 4
一笔画就yes,不一笔画no
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <climits>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
vector<int>e[1010];
int deg[1010];
bool vis[1010];
void dfs(int u) {
vis[u] =
true;
for(int i = 0; i <e[u].size(); i++) {
if(!vis[e[u][i]]) {
dfs(e[u][i]);
}
}
}
int main() {
int kase,p,q,i,u,v,temp;
bool flag;
scanf("%d",&kase);
while(kase--) {
scanf("%d%d",&p,&q);
memset(deg,0,sizeof(deg));
memset(vis,false,sizeof(vis));
for(i = 0; i < 1010;i++)
e[i].clear();
for(i = 0; i < q; i++) {
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
deg[u]++;
deg[v]++;
}
dfs(1);
flag = true;
for(i = 1; i <= p; i++)
if(!vis[i]) {
flag = false;
break;
}
if(flag) {
for(temp = 0,i = 1; i<= p; i++) {
if(deg[i]&1) {
temp++;
if(temp > 2)break;
}
}
}
if(!flag || temp > 2)puts("No");
else puts("Yes");
}
return 0;
}
12、次小生成树(v+v+value型最小花费方案个数)
/*N个不同的城市里,这些城市分别编号1~N。
如果存在两种以上的最小花费方案则输出Yes,如果最小花费的方案只有一种,则输出No)
样例输入
2(组数)
3(v)
3(e)
v+v+value
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
样例输出
No
Yes
*/
#include<iostream>
#include<stdio.h>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
#define MaxV 510
#define MaxE 200005
struct Edge
{
int x,y,dis;
};
Edge edge[MaxE],edge1[MaxE]; //分别是边数组
最小生成树中的边数组
int father[MaxV],Num[MaxV],num,dex,dey; //并查集
num统计生成树边的条数 dex dey指枚举删除边的x,y坐标
void Init(int V) //并查集初始化,单个元素自成集合
{
for(int i=1;i<=V;i++)
{
father[i]=i;
Num[i]=1;
}
}
int findfather(int x) //寻找父结点,可以压缩路径。。
{
for(x;x!=father[x];x=father[x]) ;
return father[x];
}
void Union(int x,int y)
{
int t1,t2;
t1=findfather(x);
t2=findfather(y);
if(Num[t1]>Num[t2])
{
father[t2]=t1;
Num[t1]+=Num[t2];
}
else
{
father[t1]=t2;
Num[t2]+=Num[t1];
}
}
int comp(const void* p1,const void* p2)
{
return(*(Edge*)p1).dis>(*(Edge*)p2).dis;
}
int Krusual(int V,int E)
{
int sum=0;
for(int i=1;i<=E;i++)
{
if(findfather(edge[i].x)!=findfather(edge[i].y))
{
sum+=edge[i].dis;
Union(edge[i].x,edge[i].y);
edge1[++num]=edge[i];
}
}
return sum;
}
int AKrusual(int V,int E)
{
Init(V);
int sum=0;
qsort(edge+1,E,sizeof(edge[1]),comp);
int k;
for(k=1;k<=E;++k)
{
if(findfather(edge[k].x)!=findfather(edge[k].y))
{
if(edge[k].x==dex&&edge[k].y==dey)
{continue;}
if(edge[k].x==dey&&edge[k].x==dex)
{continue;}
sum+=edge[k].dis;
Union(edge[k].x,edge[k].y);
}
}
return sum;
}
bool Judge(int V) //判断图是否连通,不连通则无法构造最小生成树
{
for(int m=1;m<=V-1;++m)
if(findfather(m)!=findfather(m+1))
return false;
return true;
}
int main()
{
int test,j,V,E;
scanf("%d",&test);
while(test--)
{
scanf("%d%d",&V,&E);
num=0;
for(j=1;j<=E;++j)
{
scanf("%d%d%d",&edge[j].x,&edge[j].y,&edge[j].dis);
}
qsort(edge+1,E,sizeof(edge[1]),comp);
Init(V);
int sum=Krusual(V,E);
int M=1000,temp;
for(int q=1;q<=num;++q)
{
dex=edge1[q].x;
dey=edge1[q].y;
temp=AKrusual(V,E);
if(temp<M&&Judge(V))
M=temp;
if(M==sum) break;
}
if(M==sum)printf("Yes\n");
else printf("No\n");
}
}
/*
13、判断最小生成树是否唯一
构成原最小生成树的边,一条边一条边的删。
*/
#include"stdio.h"
#include"stdlib.h"
struct A
{
int a,b;
int flag;
int len;
}eage[5555];
int n,m;
int set[111];
int cmp(const void *a,const void *b)
{
struct A *c,*d;
c=(struct A *)a;
d=(struct A *)b;
return c->len-d->len;
}
void build(int num)
{
int i;
for(i=1;i<=num;i++) set[i]=i;
}
int find(int k)
{
if(set[k]==k) return
k;
set[k]=find(set[k]);
return set[k];
}
void Union(int f1,int f2)
{
set[f1]=f2;
}
int Kruskal(int t_d)
{
int i;
int ans;
int f1,f2;
int count=1;
ans=0;
for(i=0;i<m;i++)
{
if(count==n)break;
if(i==t_d) continue;
f1=find(eage[i].a);
f2=find(eage[i].b);
if(f1==f2) continue;
Union(f1,f2);
eage[i].flag=1;
ans+=eage[i].len;
count++;
}
return ans;
}
int main()
{
int T;
int z,i;
int a,b,c;
int ans,t_ans;
int del[111],k;
int flag;
int temp;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
//m e n v
build(n);
for(i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
eage[i].a=a;
eage[i].b=b;
eage[i].len=c;
eage[i].flag=0;
}
qsort(eage,m,sizeof(eage[0]),cmp);
ans=Kruskal(-1);
temp=0;
for(i=1;i<=n;i++)if(set[i]==i) temp++;
if(temp>1) {printf("0\n");continue;}
k=0;
for(i=0;i<m;i++) if(eage[i].flag) del[k++]=i;
flag=0;
for(z=0;z<k;z++)
{
build(n);
t_ans=Kruskal(del[z]);
temp=0;
for(i=1;i<=n;i++)if(set[i]==i) temp++;
if(temp>1) continue;
if(t_ans==ans) {flag=1;break;}
}
if(flag) printf("Not
Unique!\n");
else printf("%d\n",ans);
}
return 0;
}
经典问题:对于每一条边存在两个权值,分别是花费和长度,要生成一个树,使得总的花费比上总的长度最小。
13、最优比率生成树
(最优比例生成树)
/*
4(v)
x y z
0 0 0
0 1 1
1 1 2
1 0 3
0
cost[i][j]=cost[j][i]=abs(z[i]-z[j]);//花费为海拔之差
dist[i][j]=dist[j][i]=sqrt(t);//距离为 点与点的水平距离
output: the minimum ratio ofoverall cost of the channels to the total length
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define N 1010
#define MAX 999999999
const double eps=1e-4;
int n;
int vis[N],x[N],y[N],z[N],pre[N];
double dis[N],cost[N][N],dist[N][N];
double prim(double x){
double totalcost=0,totaldist=0;
for(int i=1;i<=n;i++){
pre[i]=1;
}
dis[1]=0;
memset(vis,0,sizeof(vis));
vis[1]=1;
for(int i=2;i<=n;i++){
dis[i]=cost[1][i]-dist[1][i]*x;
}
int k;
for(int i=2;i<=n;i++){
double mincost=MAX;
for(int j=2;j<=n;j++){
if(!vis[j]&&dis[j]<mincost){
mincost=dis[j];
k=j;
}
}
vis[k]=1;
totalcost+=cost[pre[k]][k];
totaldist+=dist[pre[k]][k];
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]>cost[k][j]-dist[k][j]*x){
dis[j]=cost[k][j]-dist[k][j]*x;
pre[j]=k;
}
}
}
return totalcost/totaldist;
}
int main(){
while(scanf("%d",&n),n){
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x[i],&y[i],&z[i]);
for(intj=1;j<i;j++){
doublet=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
cost[i][j]=cost[j][i]=abs(z[i]-z[j]);//花费为海拔之差
dist[i][j]=dist[j][i]=sqrt(t);//距离为
点与点的水平距离
}
}
double a=0;
while(1){
double b=prim(a);
if(abs(a-b)<eps)break;
else a=b;
//cout<<a<<endl;
}
printf("%.3f\n",a);
}
return 0;
}
14、判断边与一个图最小生成shu的关系
题意:给出一张带权无向图,然后询问该图的每条边进行询问,若这条边出现在该图的所有最小生成树中,输出any;若这条边可以出现在这张图的某几个最小生成树中,输出at
least once;若这条边不会出现在这张图的任意一个最小生成树中,输出none。
/*
input
4 5
1 2 101
1 3 100
2 3 2
2 4 2
3 4 1
output
none
any
at least one
at least one
any
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100010
using namespace std;
struct Edge{
int from,to,dist,id;
}a[maxn];
int first[maxn],v[maxn*2],next[maxn*2],id[maxn*2];
int ans[maxn],fa[maxn],pre[maxn],dfs_clock,e;
int cmp(Edge a,Edge b){
return a.dist < b.dist;
}
void init(){
e = 0;
memset(first,-1,sizeof(first));
}
void add_edge(int a,int b,int ID){
v[e] = b;
next[e] = first[a];
id[e] = ID;
first[a] = e++;
}
int dfs(int u,int fa_id){
int lowu = pre[u] =++dfs_clock;
for(int i = first[u];i != -1;i= next[i]){
if(!pre[v[i]]){
int lowv =dfs(v[i],id[i]);
lowu = min(lowu,lowv);
if(lowv >pre[u]) ans[id[i]]
= 1;
}else if(pre[v[i]] <pre[u] && id[i] != fa_id){
lowu =min(lowu,pre[v[i]]);
}
}
return lowu;
}
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
void join(int x,int y){
int fx = find(x);
int fy = find(y);
if(fx != fy){
e = 0;
first[fx] = first[fy] = -1;
pre[fx] = pre[fy] = 0;
fa[fx] = fy;
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
init();
for(int i = 1;i <=n;i++) fa[i]
= i;
dfs_clock = 0;
for(int i = 0;i < m;i++){
scanf("%d%d%d",&a[i].from,&a[i].to,&a[i].dist);
a[i].id = i;
}
sort(a,a+m,cmp);
memset(pre,0,sizeof(pre));
memset(ans,0,sizeof(ans));
for(int i = 0;i < m;i++){
int j = i+1;
while(j < m &&a[i].dist == a[j].dist) j++;
for(int k = i;k <j;k++){
int fx =find(a[k].from);
int fy = find(a[k].to);
if(fx != fy){
add_edge(fx,fy,a[k].id);
add_edge(fy,fx,a[k].id);
ans[a[k].id] = 2;
}
}
for(int k = i;k <j;k++){
int fx =find(a[k].from);
int fy = find(a[k].to);
if(fx != fy &&!pre[fx]){
dfs(fx,-1);
}
}
for(int k = i;k <j;k++){
join(a[k].from,a[k].to);
}
i = j - 1;
}
for(int i = 0;i < m;i++){
if(ans[i] == 0)printf("none\n");
else if(ans[i] == 1) printf("any\n");
else printf("at
least one\n");
}
return 0;
}
15、hdu
3371 最小生成树(有重边)
/*
the cities are signed from 1 to n.
1
6( survived cities) 4(you can choose to connect the cities ) 3(stillconnected cities)
it takes c to connect p and q
p q c
1 4 2
2 6 1
2 3 5
3 4 33
the number of this connected citie(2ge 2ge 3ge)
2 1 2
2 1 3
3 4 5 6
Output
For each case, output the least money you need to take, if it’s impossible, just
output -1.
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,k,p[505],ans;
struct node{
int u,v,w;
}pt[250000];
int cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x){return p[x]==x?x:p[x]=find(p[x]);}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int i,j,num,a,b,root,x,y;
for(i=0;i<=500;i++)p[i]=i;
bool flag=false;
scanf("%d%d%d",&n,&m,&k);
for(i=1;i<=m;i++)scanf("%d%d%d",&pt[i].u,&pt[i].v,&pt[i].w);
sort(pt+1,pt+m+1,cmp);
for(i=1;i<=k;i++)
{
scanf("%d%d",&num,&root);
for(j=1;j<num;j++)
{
scanf("%d",&a);
x=find(a),y=find(root);
if(x!=y) p[x]=y;
}
}
for(num=0,i=1;i<=n;i++)if(p[i]==i) num++;
for(ans=0,i=1;i<=m;i++)
{
x=find(pt[i].u);y=find(pt[i].v);
if(x!=y)
{
p[x]=y;
ans+=pt[i].w;num--;
}
if(num==1)
{
flag=true;
break;
}
}
if(flag)printf("%d\n",ans);
elseprintf("-1\n");
}
return 0;
}
二、二分图
1、两列相同数的最大匹配数(月老的难题)
/*
1
3(3男3女)
4(可能关系)
(男女)
1 1
1 3
2 2
3 2
*/
#include<cstdio>
#include<cstring>
using namespace std;
#define N 10010
#define M 510
int head[M], next[N], key[N], num;
int match[M];
bool use[M];
void add(int u, int v) //hash表存图
{
key[num] = v;
next[num] = head[u];
head[u] = num++;
}
bool find(int u) //匈牙利算法
{
int temp;
for(int i = head[u]; i != -1;i = next[i])
{
temp = key[i];
if(!use[temp])
{
use[temp] = true;
if(match[temp]== -1 || find(match[temp])) //增广路
{
match[temp]= u;
returntrue;
}
}
}
return false;
}
int sum(int n) //最大匹配数
{
int sumall = 0;
for(int i = 1; i <= n;++i)
{
memset(use, false,sizeof(use));
if(find(i))
sumall++;
}
return sumall;
}
int main()
{
int ncase;
int allnum, relation;
int u, v;
scanf("%d",&ncase);
while(ncase--)
{
num = 0;
memset(head, -1,sizeof(head));
memset(match, -1,sizeof(match));
scanf("%d%d",&allnum, &relation);
for(int i = 0; i <relation; ++i)
{
scanf("%d%d",&u, &v);
add(u, v);
}
printf("%d\n",sum(allnum));
}
return 0;
}
三、树状数组+线段树组
/*
1、士兵杀敌<1>(树状数组模板)
/*
5(士兵数) 2(询问数)
第i个人杀的人数ai
1 2 3 4 5
第m号到第n号士兵的总杀敌数
1 3
2 4
*/
#include<iostream>
#include<cstdio>
using namespace std;
int C[1000006];//保存树状数组
int n;//元素个数(即节点个数),下标从1开始
//求最小幂2的k次幂
int lowbit(int t){ return t&(t^(t-1)); }
//求前n项和
int sum(int end)
{
int sum=0;
while(end>0)
{
sum+=C[end];
end-=lowbit(end);//或也可end^=Lowbit(end);
}
return sum;
}
//对某个元素进行加减法操作
void Plus(int pos,int num)
{
while(pos<=n)
{
C[pos]+=num;
pos+=lowbit(pos);
}
}
int main()
{
int zishu,i;
scanf("%d%d",&n,&zishu);
for(i=1;i<=n;++i)
{
int temp;
scanf("%d",&temp);//输入原始序列的第i个元素
Plus(i,temp);//
第i个元素在原来为零的基础上加上temp
}
while(zishu--)
{
int min,max;
scanf("%d%d",&min,&max);
printf("%d\n",sum(max)-sum(min-1));
}
return 0;
}
2、士兵杀敌<2>(树状数组模板)
/*
5(士兵) 6(询问)
每个士兵的杀人个数
1 2 3 4 5
QUERY 1 3
ADD 1 2
QUERY 1 3
ADD 2 3
QUERY 1 2
QUERY 1 5
*/
#include<iostream>
#include<cstdio>
using namespace std;
int C[1000006];//保存树状数组
int n;//元素个数(即节点个数),下标从1开始
//求最小幂2的k次幂
int lowbit(int t){ return t&(t^(t-1)); }
//求前n项和
int sum(int end)
{
int sum=0;
while(end>0)
{
sum+=C[end];
end-=lowbit(end);//或也可end^=Lowbit(end);
}
return sum;
}
//对某个元素进行加减法操作
void Plus(int pos,int num)
{
while(pos<=n)
{
C[pos]+=num;
pos+=lowbit(pos);
}
}
int main()
{
int zishu,i;
scanf("%d%d",&n,&zishu);
for(i=1;i<=n;++i)
{
int temp;
scanf("%d",&temp);//输入原始序列的第i个元素
Plus(i,temp);//
第i个元素在原来为零的基础上加上temp
}
while(zishu--)
{
int min,max;
char ch[10];
scanf("%s %d%d",ch,&min,&max);
if(ch[0]==‘Q‘)
printf("%d\n",sum(max)-sum(min-1));
else
Plus(min,max);
}
return 0;
}
3、士兵杀敌(三)(rmq求区间最大最小值之差)
/*
5 2
1 2 6 9 3
第一个数到第二个数之间最大最小值之差
1 2
2 4
*/
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 100010;
int maxsum[20][N], minsum[20][N]; //优化1
void RMQ(int num) //预处理->O(nlogn)
{
for(int i = 1; i != 20; ++i)
for(int j = 1; j <=num; ++j)
if(j + (1<< i) - 1 <= num)
{
maxsum[i][j]= max(maxsum[i - 1][j], maxsum[i - 1][j + (1 <<
i >> 1)]); //优化2
minsum[i][j] = min(minsum[i - 1][j],minsum[i - 1][j + (1 << i >> 1)]);
}
}
int main()
{
int num, query;
int src, des;
scanf("%d %d",&num, &query);
for(int i = 1; i <= num;++i) //输入信息处理
{
scanf("%d",&maxsum[0][i]);
minsum[0][i] = maxsum[0][i];
}
RMQ(num);
while(query--) //O(1)查询
{
scanf("%d%d", &src, &des);
int k = (int)(log(des- src + 1.0) / log(2.0));
int maxres =max(maxsum[k][src], maxsum[k][des - (1 << k) + 1]);
int minres =min(minsum[k][src], minsum[k][des - (1 << k) + 1]);
printf("%d\n",maxres - minres);
}
return 0;
}
4、第i个人的军功数
/*
4 (指令) 10(士兵)
起始时所有人的军功都是0.
ADD 1 3 10 //ADD 1 3 10表示,第1个人到第3个人请战,最终每人平均获得了10军功
QUERY 3
ADD 2 6 50
QUERY 3 //QUERY 3
表示南将军在询问第3个人的军功是多少。
样例输出
10
60
*/
#include<cstdio>
#include<cstring>
const int M=1000010;
int data[M];
int Max;
inline int LowBit(int n)
{
return n&(-n);
}
void Plus(int n,int value) //前n项每项增加value
{
while(n>0)
{
data[n]+=value;
n-=LowBit(n);
}
}
int Get(int n) //获取每个位置的值
{
int sum=0;
while(n<=Max)
{
sum+=data[n];
n+=LowBit(n);
}
return sum;
}
char cmd[50];
int main()
{
int n,a,b,v;
scanf("%d%d",&n,&Max);
while(n--)
{
scanf("%s",cmd);
if(!strcmp(cmd,"ADD"))
{
scanf("%d%d%d",&a,&b,&v);
Plus(a-1,-v);
Plus(b,v);
}
else
{
scanf("%d",&a);
printf("%d\n",Get(a));
}
}
}
五、快排
1、数组排序(快速排序稳定)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 10;
int arr[N];
/******************快速排序稳定版本*************************************/
void QuickSort(int low, int high)
{
if(low >= high)
return ;
int i, j, pivot;
i = low, j = high, pivot =arr[(high + low) >> 1]; //每次取中间元素作为基准
swap(arr[low], arr[(high +low) >> 1]);
while(i < j)
{
while(i < j&& arr[j] >= pivot) --j;
arr[i] = arr[j];
while(i < j&& arr[i] <= pivot) ++i;
arr[j] = arr[i];
}
arr[i] = pivot;
QuickSort(low, i - 1); //递归左边
QuickSort(j + 1, high); //递归右边
}
2、快速排序一般版本(可求第K大元素)
int Partition(int low, int high)
{
int i, j, pivot;
i = low, j = high, pivot =arr[low];
while(i < j)
{
while(i < j&& arr[j] >= pivot) --j;
if(i < j)swap(arr[i++], arr[j]);
while(i < j&& arr[i] <= pivot) ++i;
if(i < j)swap(arr[i], arr[j--]);
}
return j; //返回基准元素位置
}
void Quick_Sort(int low, int high)
{
int pivotpos;
if(low < high)
{
pivotpos =Partition(low, high);
Quick_Sort(low,pivotpos - 1); //基准左递归排序
Quick_Sort(pivotpos +1, high); //基准右递归排序
}
}
int select(int low, int high, int k)
{
int pivotpos, num;
if(low == high)
return arr[high];
pivotpos = Partition(low,high);
num = pivotpos - low + 1; //左边元素个数
if(k == num)
return arr[pivotpos];
else if(k < num)
return select(low,pivotpos - 1, k);
else
return select(pivotpos+ 1, high, k - num);
}
/*************************************************************************/
int main()
{
int k;
cout<<"输入10个数字:"<<endl;
for(int i = 0; i < 10;++i)
scanf("%d",&arr[i]);
QuickSort(0, 9);
cout<<"快速排序后:"<<endl;
for(int i = 0; i < 10;++i)
cout<<arr[i]<<"";
cout<<endl;
cout<<"输入第K大元素:"<<endl;
cin>>k;
cout<<select(0, 9,k)<<endl;
return 0;
}
3、归并排序求逆序数
#include <cstdio> int left[250003], right[250003]; __int64 count; void merge(int a[], int l, int m, int r) { int i, j, k, n1, n2; n1 = m - l + 1; n2 = r - m; for (i = 0; i < n1; i++) left[i] = a[l+i]; for (i = 0; i < n2; i++) right[i] = a[m+i+1]; left[n1] = right[n2] = 0x7fffffff; i = j = 0; for (k = l; k <= r; k++) { if (left[i] <= right[j]) { a[k] = left[i++]; } else { a[k] = right[j++]; count += n1 - i; } } } void mergeSort(int a[], int l, int r) { if (l < r) { int m = (l + r) / 2; mergeSort(a, l, m); mergeSort(a, m+1, r); merge(a, l, m, r); } } int main() { int n, a[500001]; while (scanf("%d", &n) && n) { count = 0; for (int i = 0; i < n; i++) scanf("%d", &a[i]); mergeSort(a, 0, n-1); printf("%I64d\n", count); } }
4、求第k大的数
#include<queue> #include<iostream> #include<vector> using namespace std; struct mycmp { bool operator()(const int &a,const int &b) { return a>b; } };//这里表示从小到大排列,最小的数在队头,随时准备走出队列 int main() { int n,k,val; char str[5]; int count; while(scanf("%d%d",&n,&k)!=EOF) { priority_queue<int,vector<int>,mycmp> pq; while(n--) { scanf("%s",str); if(str[0]==‘I‘) { scanf("%d",&val); pq.push(val); while(pq.size()>k) pq.pop(); } else { printf("%d\n",pq.top()); } } } return 0; }
5、RMQ模板
,返回区间最值 、 最值的下标
#include<string.h>
#include<stdio.h>
#include<math.h>
const
int
MAX=200005;
int
min(
int
a,
int
b){
return
a<b?a:b;}
int
dp[MAX][20],a[MAX];
int
n,k,val[MAX];
int
LOG[MAX];
void
Make_Rmq(
int
n,
int
b[])
{
int
i,j;
for
(i=1;i<=n;i++)
dp[0][i]=b[i];
for
(i=1;i<=LOG[n];i++)
{
int
limit=n+1-(1<<i);
for
(j=1;j<=limit;j++)
dp[i][j]=min(dp[i-1][j],dp[i-1][j+(1<<i>>1)]);
}
}
int
RMQ(
int
l,
int
r)
{
int
k=LOG[r-l+1];
return
min(dp[k][l],dp[k][r-(1<<k)+1]);
}
void
Make_Rmqindex(
int
n,
int
b[])
{
int
i,j;
for
(i=1;i<=n;i++)
dp[0][i]=i;
for
(i=1;i<=LOG[n];i++)
{
int
limit=n+1-(1<<i);
for
(j=1;j<=limit;j++)
{
int
x=dp[i-1][j],y=dp[i-1][j+(1<<i>>1)];
dp[i][j]=b[x]<b[y]?x:y;
}
}
}
int
Rmq_Index(
int
l,
int
r,
int
b[])
{
int
k=LOG[r-l+1];
int
x=dp[k][l];
int
y=dp[k][r-(1<<k)+1];
return
b[x]<b[y]?x:y;
}
int
main()
{
int
t,i,j;
LOG[0]=-1;
for
(i=1;i<MAX;i++)
LOG[i]=LOG[i>>1]+1;
for
(i=1;i<=10;i++)
val[i]=i;
Make_Rmqindex(10,val);
int
l,r;
while
(1)
{
scanf
(
"%d%d"
,&l,&r);
//l<r
int
ans=Rmq_Index(l,r,val);
printf
(
"%d\n"
,ans);
}
return
0;
}
6、快速幂求余模板
经改进后代码如下:(输入a,k,m,求a^k%m)
long f(long a,longk,long m)
{
long b=1;
while(k>=1)
{
if(k%2==1) b=a*b%m;
a=a*a%m;
k=k/2;
}
return b;
}
7、最长公共子序列
/*
最长公共子序列:输出s1和s2的最长公共子序列
不一定连续 如 abcde aafe 则结果为ae
*/
#include <stdio.h>
#include <string.h>
#define MAXLEN 3000//输入的字符串的最大长度
char x[MAXLEN];
char y[MAXLEN];
int b[MAXLEN][MAXLEN];//b存的是方向 用来辅助输出最后的公共字串
int c[MAXLEN][MAXLEN];//c[i][j]表示s1前i个字符和s2前j个字符的最长公共子序列
void LCSLength(int n, int m)
{
int i, j;
for(i = 0; i <= n; i++)
c[i][0] = 0;
for(j = 1; j <= m; j++)
c[0][j] = 0;
for(i = 1; i<= n; i++)
{
for(j = 1; j <= m; j++)
{
if(x[i-1] == y[j-1])//2者相等的时候则此时 c[i][j]
=c[i-1][j-1] + 1;
{
c[i][j] =c[i-1][j-1] + 1;
b[i][j] = 0;
}
else if(c[i-1][j] >=c[i][j-1])//如果不相等
则c[i][j]的值取c[i][j-1],c[i-1][j]中最大的
{
c[i][j] =c[i-1][j];
b[i][j] = 1;
}
else
{
c[i][j] =c[i][j-1];
b[i][j] = -1;
}
}
}
}
void FindLCS(int i, int j)//递归输出最长公共子序列
{
if(i == 0 || j == 0)
return;
if(b[i][j] == 0)
{
FindLCS(i-1,j-1);
printf("%c",x[i-1]);
}
else if(b[i][j] == 1)
FindLCS(i-1,j);//如果是标记为1 则剩余的未输出字符
从s1的前i-1个
s2的前j个中找出来 当递归到标记为0的时候就可以输出了
else
FindLCS(i,j-1);
}
int main(int argc, char **argv)
{
while(scanf("%s %s",x,y)!=EOF)
{
int m,n;
n = strlen(x);
m = strlen(y);
LCSLength(n,m);
FindLCS(n,m);
//printf("%d",c[n][m]);//输出公共串的最长的长度
printf("\n");
}
return 0;
}
8、快速查找素数
#include<stdio.h>
#include<string.h>
#define MAX 2000001
#define MAX_PRIME 2000001
bool nums[MAX];
int prime[MAX_PRIME];
void MakePrime()
{
int i,j;
int pl=0;
nums[0]=1,nums[1]=1;
memset(prime,true,sizeof(prime));
for(i=2;i<MAX;i++)
{
if(!nums[i])
prime[pl++]=i;
for(j=0;j<pl&&i*prime[j]<=MAX;j++)
{
nums[i*prime[j]]=1;
if(!(i%prime[j]))
break;
}
}
}
int main()
{
int n,j;
MakePrime();
while(scanf("%d",&n)!=EOF)
{
if(n>1)
{
printf("2");
for(j=1;prime[j]<=n;j++)
{
printf("%d",prime[j]);
}
}
printf("\n");
}
return 0;
}
9、表达式求值
/*
3
add(1,2)
max(1,999)
add(min(1,1000),add(100,99))
*/
#include<cstdio>
#include<iostream>
using namespace std;
char str[1000];
int start;
int val()
{
intv,n;
switch(str[start])
{
case‘m‘:start+=3;if(str[start-2]==‘i‘) return min(val(),val());else returnmax(val(),val());
case‘a‘:start+=3;return val()+val();
case‘)‘:
case‘(‘:
case‘,‘:++start;return val();
default:sscanf(str+start,"%d%n",&v,&n);start+=n;returnv;
}
}
int main()
{
intn;
scanf("%d",&n);
while(n--)
{
scanf("%s",str);
start=0;
printf("%d\n",val());
}
}
七、二分
int binarysearch(int arr[], int n, int key)
{
int low = 0, high = n;
while(low < high)
{
int mid = low + (high- low) / 2;
if(arr[mid] == key)
{
cout<<"fad";
return mid;
}
else if(arr[mid] >key)
high = mid;
else
low = mid + 1;
}
return -1;
}
八、最短路径
1、最短路径+畅通工程续
每组数据第一行是两个整数N、M(N<=100,M<=10000),
N表示成都的大街上有几个路口,
标号为1的路口是商店所在地,标号为N的路口是赛场所在地,
M则表示在成都有几条路。
N=M=0表示输入结束。
接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
Sample Input
N(有几个路口) M(有几条路)
nodenum edgenum
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
Sample Output
3
2
SPFA版本:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<queue>
#include<algorithm>
using namespace std;
#define N 110
#define MAX INT_MAX >> 1
#define CLR(arr, what) memset(arr, what, sizeof(arr))
int nodenum, edgenum;
int map[N][N], dis[N];
bool visit[N];
int SPFA(int src, int des)
{
queue<int> q;
CLR(visit, false);
for(int i = 1;i <= nodenum;++i)
dis[i] = MAX;
dis[src] = 0;
visit[src] = true;
q.push(src);
while(!q.empty())
{
int cur = q.front();
q.pop();
visit[cur] = false; //出队标记为false
for(int i = 1; i <=nodenum; ++i)
{
if(dis[i] > dis[cur]+ map[cur][i]) //没有2个集合,和Dijkstra有本质区别
{
dis[i] = dis[cur] +map[cur][i]; //能松弛就松弛
if(!visit[i]) //不在队列中则加入,然后更新所有以前经过此点的最短路径
{
q.push(i);
visit[i] =true;
}
}
}
}
return dis[des];
}
int main()
{
int start, end, cost;
int answer;
while(~scanf("%d%d",&nodenum, &edgenum) && (nodenum + edgenum))
{
for(int i = 1; i <=nodenum; ++i)
for(int j = 1; j <=nodenum; ++j)
map[i][j] = MAX;
for(int i = 0; i <edgenum; ++i)
{
scanf("%d%d%d", &start, &end, &cost);
if(cost <map[start][end])
map[start][end] =map[end][start] = cost;
}
answer = SPFA(1, nodenum);
printf("%d\n",answer);
}
return 0;
}
2、畅通工程再续
/*
3(v) 3(e)
0 1 1
0 2 3
1 2 1
0(sv) 2(ev)
城镇分别以0~N-1编号
如果不存在从S到T的路线,就输出-1.
*/
#include<iostream>
#include<stdio.h>
using namespace std;
#define inf 9999999
int map[250][250];
int n,m;
int Floyd(int x,int y)
{
int t,i,j;
for(t=0;t<n;t++)
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if(map[i][j]>map[i][t]+map[t][j])
{
map[i][j]=map[i][t]+map[t][j];
}
if(map[x][y]==inf) return -1;
else return map[x][y];
}
int main()
{
//n v,m e
while(scanf("%d%d",&n,&m)!=EOF)
{
int a,b,v,d,f,num;
for(int i=0; i<=n; i++)
for(int j=0; j<=n;j++)
map[i][j]=map[j][i]=inf;
for(inti=0;i<n;i++) //注意同一点的权值为0,刚开始wa在这儿
map[i][i]=0;
for(int i=0; i<m; i++)
{
cin>>a>>b>>v;
if(map[a][b]>v)
map[a][b]=map[b][a]=v;
}
cin>>d>>f;
num=Floyd(d,f);
cout<<num<<endl;
}
return 0;
}
3、公交车最短路径(公交站是英文名字)
输入数据有多组,每组的第一行是公交车的总数N(0<=N<=10000);
第二行有徐总的所在地start,他的目的地end;
接着有n行,每行有站名s,站名e,以及从s到e的时间整数t(0<t<100)(每个地名是一个长度不超过30的字符串)。
note:一组数据中地名数不会超过150个。
如果N==-1,表示输入结束。
Output
如果徐总能到达目的地,输出最短的时间;否则,输出“-1”。
Sample Input
6
xiasha westlake
xiasha station 60
xiasha ShoppingCenterofHangZhou 30
station westlake 20
ShoppingCenterofHangZhou supermarket10
xiasha supermarket 50
supermarket westlake 10
-1
#include<iostream>
#include<map>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int dis[155], len[155][155];
bool visit[155];
#define MAX 0x3f3f3f3f
void Dijsktra(int start, int end)
{
int k, temp;
memset(visit, 0,sizeof(visit));
for(int i = start; i <=end; ++i)
dis[i] = (i == start ?0 : MAX); //visit[start] = 1; //如果标记为1,则与temp比较的全都是MAX
for(int i = start; i <=end; ++i)
{
temp = MAX;
for(int j = start; j<= end; ++j)
if(!visit[j]&& dis[j] < temp)
temp =dis[k = j];
visit[k] = 1;
if(temp == MAX) break;
for(int j = start; j<= end; ++j)
if(dis[j] >dis[k] + len[k][j])
dis[j] =dis[k] + len[k][j];
}
}
int main()
{
int num, iterator, distance,flag;
char begin[30], end[30];
char a[30], b[30];
map<string, int>station;
while(scanf("%d",&num) != EOF && num != -1)
{
station.clear();
memset(len, MAX,sizeof(len));
flag = 0;
scanf("%s%s",begin, end);
if(strcmp(begin, end)== 0) flag = 1;
station[begin] = 1;
station[end] = 2;
iterator = 3;
for(int i = 0; i <num; ++i)
{
scanf("%s%s%d",a, b, &distance);
if(!station[a])
station[a]= iterator++;
if(!station[b])
station[b]= iterator++;
len[station[a]][station[b]]= len[station[b]][station[a]] = distance;
}
if(flag)
{
printf("0\n");
continue;
}
Dijsktra(1, iterator);
if(dis[2] == MAX) printf("-1\n");
elseprintf("%d\n",
dis[2]);
}
return 0;
}
22、最短路径 求最短距离
现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
Input
N(v)和M(e)(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。
接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。
再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。
Output
对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.
Sample Input
3 3
0 1 1
0 2 3
1 2 1
0 2
3 1
0 1 1
1 2
Sample Output
2
-1
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 210
#define MAX 9999999
#define CLR(arr, what) memset(arr,what, sizeof(arr))
int num, road;
int dis[N], maze[N][N];
bool visit[N];
void Dijkstra(int start)
{
inttemp, k;
CLR(visit,false);
for(inti = 0; i < num; ++i)
dis[i]= (i == start ? 0 : maze[start][i]);
visit[start]= true;
for(inti = 0; i < num; ++i)
{
temp= MAX;
for(intj = 0; j < num; ++j)
if(!visit[j]&& temp > dis[j])
temp= dis[k = j];
if(temp== MAX)
break;
visit[k]= true;
for(intj = 0; j < num; ++j)
if(!visit[j]&& dis[j] > dis[k] + maze[k][j])
dis[j]= dis[k] + maze[k][j];
}
}
int main()
{
int a, b, cost, start, end;
while(scanf("%d%d", &num, &road) != EOF)
{
for(int i = 0; i < N; ++i)
for(intj = 0; j < N; ++j)
maze[i][j]= MAX;
for(int i = 0; i < road; ++i)
{
scanf("%d%d%d", &a,&b, &cost);
if(cost < maze[a][b]) //一条路可以有多个cost,记录最小的。注意
maze[a][b] = maze[b][a] = cost;
}
scanf("%d%d", &start,&end);
Dijkstra(start);
if(dis[end] == MAX)
printf("-1\n");
else
printf("%d\n",dis[end]);
}
return 0;
}
4、单源最短路Dijkstra算法。只不过起点给你多个,然后求可以到达的目的地中路径最短的一条
6(6条路)
2(和草家相邻城市个数) 3(想去城市的个数)
1 3 5
1 4 7
2 8 12
3 8 4
4 9 12
9 10 2
1 2(和草家相邻)
8 9 10(想去)
输出草儿能去某个喜欢的城市的最短时间
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
usingnamespace std;
#defineMAX 0x3f3f3f3f
introad, link, want, total;
intmap[1010][1010], linkarr[1010], wantarr[1010], dis[1010];
boolvisit[1010];
voidDijkstra(int start)
{
int temp, k;
memset(visit, 0, sizeof(visit));
for(int i = 1; i <= total; ++i)
dis[i] = map[start][i];
dis[start] = 0;
visit[start] = 1;
for(int i = 1; i <= total; ++i)
{
temp = MAX;
for(int j = 1; j <= total; ++j)
if(!visit[j] && temp >dis[j])
temp = dis[k = j];
visit[k] = 1;
for(int j = 1; j <= total; ++j)
if(!visit[j] && dis[j] >dis[k] + map[k][j])
dis[j] = dis[k] + map[k][j];
}
}
intmain()
{
int x, y, cost, minn, answer;
while(scanf("%d%d%d", &road,&link, &want) != EOF)
{
total = 0;
memset(map, MAX, sizeof(map));
for(int i = 1; i <= road; ++i)
{
scanf("%d%d%d", &x,&y, &cost);
if(cost < map[x][y])
map[x][y] = map[y][x] = cost;
total = max(total, max(x, y)); //错了N久。。。。因为没有给出点的个数。
}
for(int i = 1; i <= link; ++i) //相连的城市
scanf("%d",&linkarr[i]);
for(int i = 1; i <= want; ++i) //目的地
scanf("%d", &wantarr[i]);
answer = MAX;
for(int i = 1; i <= link; ++i)//linkarr数组中所有元素中到达目的地最短的路
{
Dijkstra(linkarr[i]);
minn = MAX;
for(int j = 1; j <= want; ++j)//linkarr[i]中可以到达的目的地中最短
if(dis[wantarr[j]] < minn)
minn = dis[wantarr[j]];
if(answer > minn)
answer = minn;
}
printf("%d\n", answer);
}
return 0;
}
5、飘谊想知道他走完观光道路并回到源点的最短路径
V e
4 5
1 2 3
2 3 4
3 4 5
1 4 10
1 3 12
#include <stdio.h>
#include <string.h>
#define Maxsize 20
#define FindMin(a,b) a>b?b:a
#define INF 999999999
int g[Maxsize][Maxsize];
int degree[Maxsize];
int count;
int v[Maxsize];
int ans;
int T;
void dfs(int u,int value)
{
int i,j;
if(u==T)
{
ans=FindMin(value,ans);
// printf("%d%d\n",value);
}
for(i=0;i<count;i++)
{
if(!((u>>i)&1))
{
for(j=i+1;j<count;j++)
if(!((u>>j)&1))
{
intnext=u;
next=next|(1<<i);
next=next|(1<<j);
dfs(next,value+g[v[i]][v[j]]);
}
}
}
}
int main()
{
int n,m;
int u,e,l;
int i,j,k;
int sum=0;
while(~scanf("%d%d",&n,&m))
{
memset(degree,0,sizeof(degree));
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
g[i][j]=INF;
sum=0;
while(m--)
{
scanf("%d%d%d",&u,&e,&l);
sum+=l;
degree[u]++;degree[e]++;
g[u][e]=g[e][u]=FindMin(g[u][e],l);
}
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
g[i][j]=FindMin(g[i][j],g[i][k]+g[k][j]);
count=0;
for(i=1;i<=n;i++)
if(degree[i]&1)
v[count++]=i;
T=0;
for(i=0;i<count;i++)
{
T=(T<<1)|1;
}
ans=INF;
dfs(0,0);
printf("%d\n",sum+ans);
}
return 0;
}
6、给多个坐标,求最短路径的最大边
2
0 0(起点)
3 4(终点)
3
17 4
19 4
18 5
0
Sample Output
Scenario #1
Frog Distance = 5.000
Scenario #2
Frog Distance = 1.414
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
pair<int ,int> a[200]; //保存n个石头的坐标
double lowcost[200],closet[200];//Prim算法必备,lowcost[i]表示i距离集合的最近距离,closet[i]表示i距离集合最近的点
double map[200][200]; //两点之间的距离
int main()
{
int n;
int k=1;
while(cin>>n,n)
{
int i,j;
for (i = 0 ;i < n ; i++ )
cin>>a[i].first>>a[i].second; //输入n个点的坐标,从0开始,也就是说题目编程求0-1的最小Frog
Distance
memset(lowcost,0,sizeof(lowcost)); //清零
for ( i = 0 ; i < n ; i ++ )
{
for ( j = 0 ; j < n ; j ++ )
{//求任意两点的距离,保存到map中
map[i][j]=1.0*sqrt(pow(1.0*abs(a[i].first-a[j].first),2)+pow(1.0*abs(a[i].second-a[j].second),2));
}
}
double ans=0.0;//所要求的答案,初始化为0
for ( i = 0 ; i< n ; i++ )
{//把0放入集合,则点到集合的距离此时是点到0的距离
lowcost[i]=map[0][i];
closet[i]=0;
}
for ( i = 0 ; i < n - 1 ; i ++ )
{
doublemindis=1.0*(1<<20); //点到集合最小距离,初始化为最大
int minone; //到集合最小距离对应的点
for ( j = 0 ; j < n ; j ++ )
{
if(lowcost[j]&&mindis>lowcost[j])
{//j点不在集合中,并且j到集合的距离比最小距离还小,则更新最小距离
mindis=lowcost[j];
minone=j;
}
}
if(ans<mindis) //如果答案并不比更新的最小距离大
ans=mindis; //更新答案
lowcost[minone]=0.0;//将该点入集合
if(minone==1) //如果改点是1,则水明义江找到了答案
break;
for ( j = 0 ; j < n ; j ++ )
{//更新各点到集合的最小距离
if(map[j][minone]<lowcost[j])
{//如果minone到某点j的距离比原来的j到集合的距离要小,则更新该点到集合的距离为改点到minone的距离
lowcost[j]=map[j][minone];
closet[j]=minone;
}
}
}
cout<<"Scenario #"<<k<<endl;
printf("Frog Distance = %.3f\n\n",ans);
k++;
}
return 0;
}
7、最k短路
2(v)
2(e)
1 2 5
2 1 4
1(s)
2(end) 2(k)
#include <cstring>
#include <cstdio>
#include <queue>
#define MAXN 1005
#define MAXM 500005
#define INF 1000000000
using namespace std;
struct node
{
int v, w, next;
}
edge[MAXM], revedge[MAXM];
struct A
{
int f, g, v;
bool operator <(const A a)
const
{
if(a.f == f) return a.g < g;
return a.f < f;
}
};
int e, vis[MAXN], d[MAXN], q[MAXM *5];
int
head[MAXN], revhead[MAXN];
int n, m, s, t, k;
void init()
{
e = 0;
memset(head, -1, sizeof(head));
memset(revhead, -1, sizeof(revhead));
}
void insert(int x, int y, int w)
{
edge[e].v = y;
edge[e].w = w;
edge[e].next = head[x];
head[x] = e;
revedge[e].v = x;
revedge[e].w = w;
revedge[e].next =revhead[y];
revhead[y] = e++;
}
void spfa(int src)
{
for(int i = 1; i <= n; i++) d[i] = INF;
memset(vis, 0, sizeof(vis));
vis[src] = 0;
int h = 0, t = 1;
q[0] = src;
d[src] = 0;
while(h < t)
{
int u =
q[h++];
vis[u] = 0;
for(int i = revhead[u] ; i != -1; i
= revedge[i].next)
{
int v = revedge[i].v;
int w = revedge[i].w;
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
if(!vis[v])
{
q[t++] = v;
vis[v] = 1;
}
}
}
}
}
int Astar(int src, int des)
{
int cnt = 0;
priority_queue<A>Q;
if(src == des) k++;
if(d[src] == INF) return -1;
A t, tt;
t.v = src, t.g = 0, t.f = t.g + d[src];
Q.push(t);
while(!Q.empty())
{
tt = Q.top();
Q.pop();
if(tt.v == des)
{
cnt++;
if(cnt == k) return tt.g;
}
for(int i = head[tt.v]; i != -1; i
= edge[i].next)
{
t.v = edge[i].v;
t.g = tt.g + edge[i].w;
t.f = t.g + d[t.v];
Q.push(t);
}
}
return -1;
}
int main()
{
int x, y, w;
while(scanf("%d%d", &n, &m) != EOF)
{
init();
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &x,&y, &w);
insert(x, y, w);
}
scanf("%d%d%d", &s, &t, &k);
spfa(t);
printf("%d\n", Astar(s, t));
}
return 0;
}
/*
8、一棵树上的任意两点的距离
7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6
1 4
2 6
Sample Output
13
3
36
*/
#include<string.h>
#include<stdio.h>
#include<vector>
#include<math.h>
using namespace std;
const int M =40100;
const double inf = 1e20;
int min(int a,int b){return a<b?a:b;}
int n,k,tdfn,tot;
int dp[20][2*M],vis[M];
int B[2*M],LOG[2*M],used[M],F[2*M],pos[M];
vector<pair<int,int> > edge[2*M];
void rmq_init(int n,int num[])
{
inti,j;
for(j=1;j<=n;j++)
dp[0][j]=num[j];
for(j=1;j<=LOG[n];j++)
{
int limit=n+1-(1<<j);
for(i=1;i<=limit;i++)
{
int x=i+(1<<j>>1);
dp[j][i]=min(dp[j-1][x],dp[j-1][i]);
}
}
}
int rmq(int l,int r,int num[])
{
intm=LOG[r-l+1];
return min(dp[m][l],dp[m][r-(1<<m)+1]);
}
int sum[M];
void dfs(int s)
{
inti,t;
used[s]=1;
inttmp=++tdfn;
B[++tot]=tmp;F[tmp]=s;
pos[s]=tot;
for(i=0;i<edge[s].size();i++)
{
t=edge[s][i].first;
if(used[t]) continue;
sum[t]=sum[s]+edge[s][i].second;
dfs(t);
B[++tot]=tmp;//backtrack
}
}
int lca(int a,int b)
{
if(pos[a]>pos[b]) swap(a,b);
intans=rmq(pos[a],pos[b],B);
return F[ans];
}
int main()
{
int i,n,m,a,b,w;
LOG[0]=-1;
for(i=1;i<2*M;i++) LOG[i]=LOG[i>>1]+1;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=0;i<=n;i++) edge[i].clear();
char str[5];
for(i=0;i<m;i++)
{
scanf("%d%d%d%s",&a,&b,&w,str);
edge[a].push_back(make_pair(b,w));
edge[b].push_back(make_pair(a,w));
}
sum[1]=0;
tdfn=0;
tot=0;
memset(used,0,sizeof(used));
dfs(1);
rmq_init(tot,B);
scanf("%d",&k);
while(k--)
{
scanf("%d%d",&a,&b);
printf("%d\n",sum[a]+sum[b]-2*sum[lca(a,b)]);
}
}
return 0;
}
9、最短路径spfa
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<queue>
#include<algorithm>
using namespace std;
#define N 110
#define MAX INT_MAX >> 1
#define CLR(arr, what) memset(arr, what,sizeof(arr))
int nodenum, edgenum;
int map[N][N], dis[N];
bool visit[N];
int SPFA(int src, int des)
{
queue<int> q;
CLR(visit, false);
for(int i = 1;i <= nodenum; ++i)
dis[i] = MAX;
dis[src] = 0;
visit[src] = true;
q.push(src);
while(!q.empty())
{
int cur = q.front();
q.pop();
visit[cur] = false; //出队标记为false
for(int i = 1; i <= nodenum; ++i)
{
if(dis[i] > dis[cur] + map[cur][i])
//没有2个集合,和Dijkstra有本质区别
{
dis[i] = dis[cur] +map[cur][i];
//能松弛就松弛
if(!visit[i]) //不在队列中则加入,然后更新所有以前经过此点的最短路径
{
q.push(i);
visit[i] = true;
}
}
}
}
return dis[des];
}
int main()
{
intstart, end, cost;
intanswer;
while(~scanf("%d%d", &nodenum,&edgenum)
&& (nodenum + edgenum))
{
for(int i = 1; i <= nodenum; ++i)
for(int j = 1; j <= nodenum; ++j)
map[i][j] = MAX;
for(int i = 0; i < edgenum; ++i)
{
scanf("%d%d%d", &start, &end, &cost);
if(cost < map[start][end])
map[start][end] =map[end][start]
= cost;
}
answer = SPFA(1, nodenum);
printf("%d\n", answer);
}
return 0;
}
10、aij矩阵类型的单源最短路径并且输出最短权值和最短路径
/*
起点终点不用tax
5(v)
aij(i到j)
0 3 22 -1 4
3 0 5 -1 -1
22 5 0 9 20
-1 -1 9 0 4
4 -1 20 4 0
各自的tax
5 17 8 3 1
1 3
3 5
2 4
-1 -1
0
*/
#include"stdio.h"
#include"string.h"
int n;
int tax[111];
int map[111][111];
int path[111][111];
void floyd()
{
inttemp;
intk,i,l;
for(i=1;i<=n;i++)
for(l=1;l<=n;l++)
path[i][l]=l;
for(k=1;k<=n;k++)
{
for(i=1;i<=n;i++)
{
for(l=1;l<=n;l++)
{
temp=map[i][k]+map[k][l]+tax[k];
if(temp<map[i][l])
{
map[i][l]=temp;
path[i][l]=path[i][k];
}
elseif(temp==map[i][l])
{
if(path[i][l]>path[i][k])
path[i][l]=path[i][k];
}
}
}
}
}
int main()
{
inti,l;
inttemp;
ints,e;
while(scanf("%d",&n),n)
{
for(i=1;i<=n;i++)
for(l=1;l<=n;l++)
{
scanf("%d",&temp);
if(temp==-1)map[i][l]=11111111;
else map[i][l]=temp;
}
for(i=1;i<=n;i++)scanf("%d",&tax[i]);
floyd();
while(scanf("%d%d",&s,&e)!=-1)
{
if(s==-1&& e==-1) break;
printf("From%d to %d :\n",s,e);
printf("Path:%d",s);
temp=s;
while(temp!=e)
{
printf("-->%d",path[temp][e]);
temp=path[temp][e];
}
printf("\n");
printf("Totalcost : %d\n\n",map[s][e]);
}
}
return0;
}
11、邻接矩阵的单源最短路径
/*
5 7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
60
*/
#include <iostream>
#include<cstdio>
using namespace std;
#define MAX 9999999
#define LEN 210
int map[LEN][LEN]; //某点到某点两点间的的距离
int dist[LEN]; //记录当前点到源点的最短路径长度
int mark[LEN]; //加入进来的点的集合
//初始化map为正无穷大
void init()
{
int i,j;
for(i=0;i<LEN;i++)
{
for(j=0;j<LEN;j++)
{
map[i][j]=MAX;
}
}
}
//n:多少条路
start:起始点
//dist[i],最后存储着start到i点的最短距离
void myDijstra(int n,int start)
{
int i,j,min,pos;
for(i=1;i<=n;i++)
{
mark[i]=0;//没有点加入
dist[i]=map[start][i];//把start附近点
dis[]初始化
}
mark[start]=1;//把起始点加进来
dist[start]=0;
for(i=1;i<=n;i++)
{
min=MAX;
for(j=1;j<=n;j++)
{
if(!mark[j] &&dist[j]<min)
{ //取出不在mark里的最小的dist[i]
min=dist[j];
pos=j;//标记
}
}
if(min==MAX)//已经不能通了
break;
mark[pos]=1;//把K加进来
//做松弛操作
for(j=1;j<=n;j++)
{
if(!mark[j] &&dist[j]>dist[pos]+map[pos][j])//start->j or start->pos,pos->j
{
dist[j]=dist[pos]+map[pos][j];//这步跟prim算法有点不同
}
}
}
}
int main(){
int i,n,line;
int a,b,d;
scanf("%d%d",&n,&line);//输入点和
init();
for(i=0;i<line;i++)
{
scanf("%d%d%d",&a,&b,&d); //输入各边的权值
// 这个判断是防止重边的,因为也许会对同一条路给出两个值,这时就保存较小的值
if(map[a][b]>d)
{
map[a][b]=map[b][a]=d;
}
}
myDijstra(n,1);//调用方法(点数,起始点)
//输出1到5的最短路径
cout<<dist[5]<<endl;
return 0;
}
九、最大流
1、简单最大流
2
V e
3 2
1 2 1
2 3 1
3 3
1 2 1
2 3 1
1 3 1
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
#define N 100
#define MAX 1<<28
int cost[N][N];
int flow[N], pre[N];
bool visit[N];
int n, m;
int BFS()
{
int temp;
queue<int> q;
visit[1] = true;
memset(pre, -1, sizeof(pre));
memset(visit, false,sizeof(visit));
for(int i = 1; i <= n;++i)
flow[i] = MAX;
q.push(1);
while(!q.empty())
{
temp = q.front();
q.pop();
if(temp == n)
break;
for(int i = 1; i <=n; ++i) //寻找增广路最小流量
{
if(!visit[i]&& cost[temp][i] > 0)
{
visit[i]= true;
flow[i]= min(flow[temp], cost[temp][i]);
pre[i] =temp; //更新路径
q.push(i);
}
}
}
if(!visit[n] || n == 1) //找不到完整增广路or源点汇点重合
return -1;
else
return flow[n];
}
int EK()
{
int temp, d, res, maxflow;
maxflow = 0;
while( (d = BFS() ) != -1)
{
maxflow += d;
temp = n;
while(temp != 1)
{
res =pre[temp];
cost[res][temp]-= d; //正向边
cost[temp][res]+= d; //反向边
temp = res;
}
}
return maxflow;
}
int main()
{
int ncase, T = 1;
int start, end, capacity;
scanf("%d",&ncase);
while(ncase--)
{
memset(cost, 0,sizeof(cost));
// v e
scanf("%d%d", &n, &m);
for(int i = 1; i <=m; ++i)
{
scanf("%d%d%d",&start, &end, &capacity); //重边叠加
cost[start][end]+= capacity;
}
printf("Case %d:%d\n", T++, EK());
}
return 0;
}
2、现在要求从1点到n点在从n点返回1点的最短路
/*
从自己的房子出发到农场,再从农场返回自己的房子,要求去回不走同一条路。
房子的点数为1,农场为n,在1到n之间有很多点,给出n个顶点,m条边,然后m行每行有三个数,
a,b,c代表a到c的路径长度为c,并且a到b是无向边,
现在要求从1点到n点在从n点返回1点的最短路
4v(n) 5e(m)
a b c
1 2 1
2 3 1
3 4 1
1 3 2
2 4 2
*/
/*
Memory 968K
Time 0MS
*/
#include <iostream>
#include <queue>
using namespace std;
#define INF INT_MAX
#define min(a,b) (a>b?b:a)
#define MAXV 1100
#define MAXM 40100
typedef struct{
int s,t,next,w,r;
}Edge;
Edge edge[MAXM];
int source,sink,n,m,mincost,edgesum;
int head[MAXV],d[MAXV],parent[MAXV];
void addedge(int a,int b,int c,int r){
edge[edgesum].s=a;
edge[edgesum].t=b;
edge[edgesum].r=r;
edge[edgesum].w=c;
edge[edgesum].next=head[a];
head[a]=edgesum++;
edge[edgesum].s=b;
edge[edgesum].t=a;
edge[edgesum].r=0;
edge[edgesum].w=-c;
edge[edgesum].next=head[b];
head[b]=edgesum++;
}
int spfa(){
queue <int>q;
int v,i,tmp;
bool vis[MAXV];
for(i=0;i<=sink;i++)d[i]=INF;
memset(vis,false,sizeof(vis));
memset(parent,-1,sizeof(parent));
q.push(source);
vis[source]=true;
d[source]=0;
while(!q.empty()){
v=q.front();q.pop();
vis[v]=false;
for(i=head[v];i!=-1;i=edge[i].next){
tmp=edge[i].t;
if(edge[i].r&& edge[i].w+d[v]<d[tmp]){
d[tmp]=edge[i].w+d[v];
parent[tmp]=i;
if(!vis[tmp]){
q.push(tmp);
vis[tmp]=true;
}
}
}
}
return 0;
}
void MCMF(){
int u;
mincost=0;
while(1){
spfa();
if(parent[sink]==-1)break;
u=parent[sink]; //这里不用求出增光路径上的最小流量
while(u!=-1){ //因为最小流量一定为1
edge[u].r--;
edge[u^1].r++;
u=parent[edge[u].s];
}
mincost+=d[sink];
}
}
int main(){
int i,a,b,c;
while(~scanf("%d%d",&n,&m)){
memset(head,-1,sizeof(head));
edgesum=0,source=0,sink=n+1;
for(i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
addedge(a,b,c,1);
addedge(b,a,c,1);
}
addedge(source,1,0,2);
addedge(n,sink,0,2);
MCMF();
printf("%d\n",mincost);
}
return 0;
}
十二、贪心问题
4、拦截导弹
#include<stdio.h>
#include<string.h>
int main()
{
intt,n,i,j,maxdp;
scanf("%d",&t);
while(t--)
{
intdp[25]={0},a[25]={0},b[25]={0};
scanf("%d",&n);
for(i=0;i<n;++i)
scanf("%d",&a[i]);
for(i=0,j=n-1;i<n&&j>=0;++i,--j)
b[i]=a[j];
maxdp=0;
for(i=0;i<n;++i)
{
dp[i]=1;
for(j=0;j<i;++j)
{
if(b[i]>b[j]&&dp[i]<dp[j]+1)
dp[i]=dp[j]+1;
}
if(maxdp<dp[i])
maxdp=dp[i];
}
printf("%d\n",maxdp);
}
return0;
}
5、最少拦截系统
#include<iostream>
using namespace std;
int main()
{
int n,i,j,a;
int sum[10002];
while(cin>>n)
{
for(i=0;i<=10001;i++)
sum[i]=400000;
//8 389 207 155 300 299 170 158 65
for(i=0;i<n;i++)
{
cin>>a;
j=0;
while(sum[j]<a)
{
j++;
}
sum[j]=a;
}
int total=0;
for(i=0;i<10001;i++)
if(sum[i]<400000)
total++;
cout<<total<<endl;
}
return 0;
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5、心急的c小佳
if you have five sticks whose pairs oflength and weight are ( 9 , 4 ) , ( 2 , 5 ) , ( 1 , 2 )
, ( 5 , 3 ) , and ( 4 ,1 ) ,
then the minimum setup time should be 2minutes since there is a sequence of pairs ( 4 , 1 ) ,
( 5 , 3 ) , ( 9 , 4 ) ,( 1 , 2 ) , ( 2 , 5 ) .
Sample Input
3
5
4 9 5 2 2 1 3 5 1 4
3
2 2 1 1 2 2
3
1 3 2 2 3 1
Sample Output
2
1
3
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct point{
intstart,end;
intvisit;
};
bool cmp(point x,point y)
{
returnx.start<y.start;
}
int main()
{
pointa[5005];
intt,n,i,j,count;
cin>>t;
while(t--)
{
memset(a,0,sizeof(a));
count=0;
j=0;
intkstart,kend;
cin>>n;
for(i=0;i<n;++i)
{
cin>>a[i].start;
cin>>a[i].end;
a[i].visit=1;
}
sort(a,a+n,cmp);
while(count<n)
{
kstart=-1;kend=-1;
for(i=0;i<n;i++)
{
if(a[i].start>=kstart&&a[i].end>=kend&&a[i].visit)//并且没访问过
{
count++;
a[i].visit=0;
kstart=a[i].start;
kend=a[i].end;
}
}
j++;
}
cout<<j<<endl;
}
return0;
}
点评:上面两个题做法是一致的,都是进行标记,进行暴力搜索,很好的贪心的题。
7、radar
/*
题意:给定点集S={(xi,yi)i=1.2.3...n},求用圆心在x轴上,半径为r的圆覆盖S所需的圆的最少个数。
解题思路:先把给定的xi,yi,r转化为x轴上的区间,即圆心所在的区间,这样就转化为了区间选点问题。
先对右端点从小到大排序,右端点相同时,左端点从小到大排序。
*/
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct radar{
doublea,b;
}r[1005];
bool comp(radar a1,radar a2)
{
if(a1.b!=a2.b)//对右端点从小到大排序
returna1.b<a2.b;
returna1.a<a2.a;//右端点相同,左端点从小到大排序
}
int main()
{
intn,d,i,cas=0;
while(scanf("%d%d",&n,&d),n,d)
{
doublex,y;
intflag=0,m=0;
for(i=0;i<n;++i)
{
scanf("%lf%lf",&x,&y);
if(fabs(y)>d)
{
flag=1;//高度大于半径,有覆盖不到的点
continue;
}
doublediff=sqrt(d*d-y*y);
r[m].a=x-diff;
r[m++].b=x+diff;
}
printf("Case%d:
",++cas);
if(flag)
{
printf("-1\n");
continue;
}
sort(r,r+m,comp);
intcnt=1,p=0;
for(i=1;i<m;++i)
{
if(r[i].a<=r[p].b)
{
continue;
//区间重叠,不换
雷达
}
else{
cnt++;
p=i;
}
}
printf("%d\n",cnt);
}
return0;
}
十三、几何问题
1、点线距离
inline double PPdis(Point a,Point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline double PLdis(Point a,Point l1,Point l2){
return fabs(Cross(a,l1,l2)/PPdis(l1,l2));
}
2、是否是凸多边形
inline int _sgn(double x){return fabs(x) < eps ? 0 : (x > 0 ? 1 : 2);}int is_convex(int n,Point P[]){//允许相邻边共线,顶点按顺时针或者逆时针给出
int i,s[3]={1,1,1};
for(i=0;i<n&&s[1]|s[2];i++)
s[_sgn(Cross(P[i],P[(i+1)%n],P[(i+2)%n]))]=0;
return s[1]|s[2];
}
3、点是否在凸多边形内或边上
int inside_convex(Point q,int n,Point *p){
int i,s[3]={1,1,1};
for(i=0;i<n&&s[1]|s[2];i++)
s[_sgn(Cross(p[i],p[(i+1)%n],q))]=0;
return s[1]|s[2];
}
4、点在任意多边形内或边上
#include <math.h>
#include <cstdio>
#include<algorithm>using namespace std;const int maxn = 100010;const double eps = 1e-8;
inline double sgn(double x) {return fabs(x)<eps?0:(x>0?1:-1);}struct point{
double x,y;
bool operator == (const point& t) const {
return sgn(x-t.x)==0 && sgn(y-t.y)==0;
}
}p[maxn],set[maxn];
inline double cross(point a,point b,point c){return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);}bool dotOnSeg(point p, point s, point e) {
if ( p == s || p == e ) return true;
return sgn(cross(s,e,p))==0 && sgn((p.x-s.x)*(p.x-e.x))<=0 && sgn((p.y-s.y)*(p.y-e.y))<=0;
} bool point_in_polygon(point o, point* p, int n) {
int i, t;
point a, b;
p[n] = p[0]; t = 0;
for (i=0; i < n; i++) {
if ( dotOnSeg(o, p[i], p[i+1]) ) return true;
a = p[i]; b = p[i+1];
if ( a.y > b.y ) {
point tmp = a; a = b; b = tmp;
}
if ( cross(o, a, b) < -eps && a.y < o.y-eps && o.y < b.y+eps )
t++;
}
return t&1;
}
十四、kmp算法
1、最小的匹配位置
/*
a[1], a[2], ...... , a[N]
b[1], b[2], ...... , b[M]
存在k使得: a[K] = b[1], a[K + 1] = b[2],
...... ,a[K + M - 1] = b[M]
If there are more than one K exist, output the smallest one.
2(组数)
13 5
1 2 1 2 3 1 2 3 1 3 2 1 2
1 2 3 1 3
*/
#include<cstdio>
#include<cstring>
int n,m;
int a[1000010],b[10010];
int p[11111];
void getp(){
p[1]=0;
int i,j=0;
for(i=2;i<=m;i++){
while(j>0&&b[j+1]!=b[i]) j=p[j];
if(b[j+1]==b[i]) j+=1;
p[i]=j;
}
}
int kmp()
{
int i,j=0,cnt=0;
for(i=1;i<=n;i++){
while(j>0&&b[j+1]!=a[i]) j=p[j];
if(b[j+1]==a[i]) j+=1;
if(j==m){
return i-j+1;
j=p[j];
}
}
return -1;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(intj=1;j<=m;j++) scanf("%d",&b[j]);
getp();
printf("%d\n",kmp());
}
}
2、子串出现的次数
/*
1组数
AZA w
AZAZAZA T
3
on a single line: the number of occurrences of the word W in the text T.
*/
#include<cstdio>
#include<cstring>
int n,m;
char a[1000010],b[10010];
int p[11111];
void getp(){
p[1]=0;
int i,j=0;
for(i=2;i<=m;i++){
while(j>0&&b[j+1]!=b[i]) j=p[j];
if(b[j+1]==b[i]) j+=1;
p[i]=j;
}
}
int kmp()
{
int i,j=0,cnt=0;
for(i=1;i<=n;i++){
while(j>0&&b[j+1]!=a[i]) j=p[j];
if(b[j+1]==a[i]) j+=1;
if(j==m){
cnt++;
j=p[j];
}
}
return cnt;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s%s",b+1,a+1);
m=strlen(b+1);
n=strlen(a+1);
getp();
printf("%d\n",kmp());
}
}
3、简单求周期
/*
abcd
aaaa
ababab
1
4
3
*/
#include<cstdio>
#include<cstring>
char b[1000010];
int p[1000010];
int m,n;
void getp(){
p[1]=0;
int i,j=0;
for(i=2;i<=m;i++){
while(j>0&&b[j+1]!=b[i]) j=p[j];
if(b[j+1]==b[i]) j+=1;
p[i]=j;
}
}
int main(){
while(scanf("%s",b+1)!=EOF)
{
if(b[1]==‘.‘) break;
m=strlen(b+1);
getp();
if(m%(m-p[m])==0)printf("%d\n",m/(m-p[m]));
elseprintf("1\n");
}
}
/*
4、求最长的公共子串,要注意的是字典序最小
3
3
CATCATCATCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
ACATCATCATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AACATCATCATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
*/
#include<cstdio>
#include<cstring>
char str[20][70];
char tmp[70];
int p[70];
void getp(int m,char *b1){
char b[70];
strcpy(b+1,b1);
p[1]=0;
int i,j=0;
for(i=2;i<=m;i++){
while(j>0&&b[j+1]!=b[i]) j=p[j];
if(b[j+1]==b[i]) j+=1;
p[i]=j;
}
}
bool kmp(char *a1,char *b1,int n,int m)
{
char a[70],b[70];
strcpy(a+1,a1);strcpy(b+1,b1);
int i,j=0,cnt=0;
for(i=1;i<=n;i++){
while(j>0&&b[j+1]!=a[i]) j=p[j];
if(b[j+1]==a[i]) j+=1;
if(j==m){
return true;
}
}
return false;
}
bool check(char *s,int tot)
{
int i,j;
for(i=2;i<=tot;i++)
{
intn=strlen(str[i]+1),m=strlen(s);
if(!kmp(str[i]+1,s,n,m))
return false;
}
return true;
}
char ans[70];
int main()
{
int t,n,i,j;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%s",str[i]+1);
int len=strlen(str[1]+1);
int L=0;
for(i=1;i<=len;i++)
{
for(j=1;j<=len-i+1;j++)
{
strncpy(tmp,str[1]+j,i);
memset(p,0,sizeof(p));
getp(i,tmp);
if(check(tmp,n))
{
if(i>=L)
{
if(strcmp(tmp,ans)<0&&i==L) strcpy(ans,tmp);
if(i>L) strcpy(ans,tmp);
L=strlen(ans);
}
}
memset(tmp,0,sizeof(tmp));
}
}
if(L>=3) puts(ans);
else puts("no significantcommonalities");
}
}
5、kmp算法模板(子串个数)
#include<cstdio>
#include<cstring>
int n,m;
char a[1000],b[1000];
int p[1000];
void getp(){
p[1]=0;
int i,j=0;
for(i=2;i<=m;i++){
while(j>0&&b[j+1]!=b[i]) j=p[j];
if(b[j+1]==b[i]) j+=1;
p[i]=j;
}
}
int kmp()
{
int i,j=0,cnt=0;
for(i=1;i<=n;i++){
while(j>0&&b[j+1]!=a[i]) j=p[j];
if(b[j+1]==a[i]) j+=1;
if(j==m){
cnt++;
j=p[j];
}
}
return cnt;
}
int main()
{
while(scanf("%s%s",a+1,b+1)!=EOF)
{
m=strlen(b+1);
n=strlen(a+1);
getp();
printf("%d\n",kmp());
}
}
/*
6、求S中最长回文串的长度.
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,
回文就是正反读都是一样的字符串,如aba, abba等
aaaa
abab
4
3
*/
#include<cstdio>
#include<cstring>
const int M = 110010*2;
char str[M];//start from index 1
int p[M];
char s[M];
int n;
void checkmax(int &ans,int b){
if(b>ans) ans=b;
}
inline int min(int a,int b){
return a<b?a:b;
}
void kp(){
int i;
int mx = 0;
int id;
for(i=1; i<n; i++){
if( mx > i )
p[i] = min( p[2*id-i], p[id]+id-i );
else
p[i] = 1;
for(; str[i+p[i]] == str[i-p[i]]; p[i]++) ;
if( p[i] + i > mx ) {
mx = p[i] + i;
id = i;
}
}
}
void pre()
{
int i,j,k;
n = strlen(s);
str[0] = ‘$‘;
str[1] = ‘#‘;
for(i=0;i<n;i++)
{
str[i*2 + 2] = s[i];
str[i*2 + 3] = ‘#‘;
}
n = n*2 + 2;
str[n] = 0;
}
void pt()
{
int i;
int ans = 0;
for(i=0;i<n;i++)
checkmax(ans, p[i]);
printf("%d\n", ans-1);
}
int main()
{
int T,_=0;
while( scanf("%s", s) !=EOF )
{
pre();
kp();
pt();
}
return 0;
}
7、容斥定理
/*
he wants to fire the people whose work number is co-prime with n next year.
2
4
5
82
354
Hint
Case1: sum=1+3*3*3*3=82
Case2: sum=1+2*2*2*2+3*3*3*3+4*4*4*4=354
*/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
const __int64 mod = 1000000007;
__int64 n;
__int64 power(__int64 a,__int64 b,__int64 c)
{
__int64 ret=1;
while(b>0)
{
if(b&1)
ret=ret*a%c;
a=a*a%c;
b>>=1;
}
return ret;
}
__int64 calc(__int64 n)
{
__int64 a=n,b=(2*n+1),c=(n+1),d=(3*n*n+3*n-1)%mod;
__int64 sum=1;
sum*=a;sum%=mod;
sum*=b;sum%=mod;
sum*=c;sum%=mod;
sum*=d;sum%=mod;
__int64 t=power(30,mod-2,mod);
return sum*t%mod;
}
__int64 sici(__int64 n)
{
__int64 sum=1;
for(int i=1;i<=4;i++)
{
sum*=n;
if(sum>=mod) sum%=mod;
}
return sum;
}
__int64 solve(__int64 r,__int64 n)
{
vector<__int64> p;
__int64 i;
for(i=2;i*i<=r;i++)
{
if(r%i==0)
{
p.push_back(i);
while(r%i==0) r/=i;
}
}
if(r>1) p.push_back(r);
__int64 sum=0;
for(__int64 num=1;num<(1<<p.size());num++)
{
__int64 mult=1,ones=0;
for(i=0;i<p.size();i++)
{
if(num&(1<<i))
{
ones++;
mult*=p[i];
if(mult>=mod) mult%=mod;
}
}
if(ones%2) sum+=sici(mult)*calc(n/mult);
else sum-=sici(mult)*calc(n/mult);
sum%=mod;
}
return sum;
}
int main()
{
__int64 t,i,j;
scanf("%I64d",&t);
while(t--)
{
scanf("%I64d",&n);
printf("%I64d\n",(calc(n)-solve(n,n)%mod+mod)%mod);
}
return 0;
}
/*
8、字典树的简单应用
banana
band
bee
absolute
acm
ba
b
band
abc
2
3
1
0
*/
#include<stdio.h>
#include<string.h>
char s[15];
class trie{
public :
int num;
trie* child[27];
trie()
{
num=0;
memset(child,0,sizeof(child));
}
}root;
void insert(char *s)
{
class trie *cur=&root;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int id=s[i]-‘a‘;
if(!cur->child[id])
cur->child[id]=new trie;
cur=cur->child[id];
cur->num++;
}
}
int find(char *s)
{
class trie *cur=&root;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int id=s[i]-‘a‘;
if(!cur->child[id]) return 0;
cur=cur->child[id];
}
return cur->num;
}
int main()
{
while(gets(s))
{
if(strcmp(s,"")==0) break;
insert(s);
}
while(scanf("%s",s)!=EOF)
printf("%d\n",find(s));
return 0;
}
8、括号匹配
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 10005
char a[10005];
typedef struct{
char*top;
char*base;
intstacksize;
}SqStack;
void InitStack(SqStack *s)
{
s->base=(char*)malloc(N*sizeof(char));
s->top=s->base;
s->stacksize=N;
}
void Push(SqStack *s,char e)
{
*s->top=e;
s->top++;
}
char Pop(SqStack *s)
{
--s->top;
return*s->top;
}
char GetTOP(SqStack *s)
{
return*(s->top-1);
}
int matching(char a[])
{
inti,len;
len=strlen(a);
SqStacks;
InitStack(&s);
for(i=0;i<len;++i)
{
if(a[i]==‘(‘||a[i]==‘[‘)
Push(&s,a[i]);
elseif(a[i]==‘)‘)
{
if(s.top==s.base)
return0;
elseif(GetTOP(&s)==‘(‘)
Pop(&s);
else
return 0;
}
elseif(a[i]==‘]‘)
{
if(s.top==s.base)
return0;
elseif(GetTOP(&s)==‘[‘)
Pop(&s);
else
return 0;
}
}
if(s.top==s.base)
return1;
else
return0;
}
int main()
{
intzushu;
scanf("%d",&zushu);
while(zushu--)
{
scanf("%s",a);
if(matching(a))
printf("Yes\n");
else
printf("No\n");
}
return0;
}
/*
9、求一个字符串的最长递增子序列的长度
如:dabdbf最长递增子序列就是abdf,长度为4
*/
#include<stdio.h>
int length(char * s)
{
intlen[128] = {0}, i, t;
for(;*s != ‘\0‘ && (t = len[*s
- 1] + 1); s++)
for(i= *s; i < 128 && len[i] <
t; len[i++] = t);
returnlen[127];
}
int main()
{
intn;
chars[10001];
for(scanf("%d\n",&n); n--;)
printf("%d\n",length(gets(s)));
return0;
}