1、模板题 我是用prim搞得 给出每点坐标求最小生成树
hdu1162Eddy's picture 最小生成树
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int flag1=0;
double sum;
double arr_list[110][110];
struct Edge
{
int point;
double lowcost;
int flag;
};
Edge edge[12100];
struct Point
{
double x,y;
}point[110];
double prim(int n)
{
int i,j,k=1,flag;
double min,sum2=0;
j=1;
for(i=1;i<=n;i++)
{
if(i!=j)
{
edge[i].point=i;
edge[i].lowcost=arr_list[j][i];
edge[i].flag=0;
}
}
edge[j].lowcost=0;
edge[j].flag=1;
for(i=2;i<=n;i++)
{
k=1;
min=65535;
flag=0;
for(j=2;j<=n;j++)
{
if(edge[j].flag==0&&edge[j].lowcost<min)
{
k=j;
min=edge[j].lowcost;
flag=1;
}
}
if(!flag) return -1;
sum2+=min;
edge[k].flag=1;
for(j=2;j<=n;j++)
{
if(edge[j].flag==0&&arr_list[k][j]<edge[j].lowcost)
{
edge[j].point=k;
edge[j].lowcost=arr_list[k][j];
}
}
}
return sum2;
}
int main()
{
// freopen("cin.txt","r",stdin);
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
{
cin>>point[i].x>>point[i].y;
arr_list[i][i]=65535;
}
for(int i=1;i<n;i++)
{
for(int j=i+1;j<=n;j++)
{
arr_list[i][j]=sqrt(pow((point[i].x-point[j].x),2)+pow((point[i].y-point[j].y),2));
arr_list[j][i]=arr_list[i][j];
//cout<<arr_list[i][j]<<endl;
}
}
sum=prim(n);
printf("%.2lf\n",sum);
}
return 0;
}
2、模板题 floyd最短路相加改成相乘
#include <iostream>
#include<cstdio>
using namespace std;
double dist[1005][1005];
int main()
{
// freopen("cin.txt","r",stdin);
int n,q,a,b;
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
scanf("%lf",&dist[i][j]);
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
for(int k=0;k<n;k++)
{
if(dist[j][i]*dist[i][k]>dist[j][k])
dist[j][k]=dist[j][i]*dist[i][k];
}
}
}
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&a,&b);
a--;b--;
if(dist[a][b]>0.000001)
printf("%.3lf\n",dist[a][b]);
else puts("What a pity!");
}
}
return 0;
}
3、稍微转弯的prim 题意:p个哨所间只有s个卫星通道,但每个哨所都有无线发射器,无线发射的功率随长度增加而增加但是每个都一样,问最小长度?
我们知道prim算法中的步骤是每次加入最小的边,但是并没有将加入的边存储下来,加一个数组存储下来,倒序排序,前s个边用卫星通道就好啦
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int flag1=0;
int n,s,p;
double sum;
double arr_list[504][504];
double d[503];
struct Edge
{
int point;
double lowcost;
int flag;
};
Edge edge[505];
bool cmp(double a,double b)
{
return a>b;
}
struct Point
{
double x,y;
}point[502];
void prim(int p)
{
int i,j,k=1;
double min,sum2=0;
j=1;
for(i=1;i<=p;i++)
{
if(i!=j)
{
edge[i].point=j;
edge[i].lowcost=arr_list[j][i];
edge[i].flag=0;
}
}
edge[j].lowcost=0;
edge[j].flag=1;
int l=0;
for(i=1;i<p;i++)
{
min=65535000;
for(j=2;j<=p;j++)
{
if(edge[j].flag==0&&edge[j].lowcost<min)
{
k=j;
min=edge[j].lowcost;
}
}
d[l++]=arr_list[k][edge[k].point];
//sum2+=min;
edge[k].flag=1;
for(j=2;j<=p;j++)
{
if(edge[j].flag==0&&arr_list[k][j]<edge[j].lowcost)
{
edge[j].point=k;
edge[j].lowcost=arr_list[k][j];
}
}
}
}
int main()
{
// freopen("cin.txt","r",stdin);
cin>>n;
while(n--)
{
cin>>s>>p;
for(int i=1;i<=p;i++)
{
cin>>point[i].x>>point[i].y;
arr_list[i][i]=65535000;
}
for(int i=1;i<p;i++)
{
for(int j=i+1;j<=p;j++)
{
arr_list[i][j]=sqrt(pow((point[i].x-point[j].x),2)+pow((point[i].y-point[j].y),2));
arr_list[j][i]=arr_list[i][j];
//cout<<arr_list[i][j]<<endl;
}
}
prim(p);
sort(d,d+p-1,cmp);
cout.setf(ios::fixed);//保留两位小数
cout.precision(2);
cout<<d[s-1]<<endl;
}
return 0;
}
4、多源点单汇点 之前都是单源最短路,如果是多源单汇点的话也是一样的
hdu2680Choose the best route dijkstra
#include <iostream>
#include<cstdio>
#include<cstring>
#define MaxN 10010
#define MaxInt 2000000
using namespace std;
int map[MaxN][MaxN],dist[MaxN],start;
bool mark[MaxN];
int n,m,s,p,q,t,w,b,end,min1,minn,ww;
void dijkstra(int s)
{
memset(mark,0,sizeof(mark));
for(int i=1;i<=n;i++) dist[i]=map[s][i];
mark[s]=1;dist[s]=0;
int k;
for(int i=1;i<n;i++)
{
minn=MaxInt;
for(int j=1;j<=n;j++)
{
if(!mark[j]&&minn>dist[j])
{
k=j;
minn=dist[j];
}
}
if(minn==MaxInt) break;
mark[k]=1;
for(int j=1;j<=n;j++)
{
if(!mark[j]&&dist[j]>dist[k]+map[k][j])
{
dist[j]=dist[k]+map[k][j];
}
}
}
}
int main()
{
// freopen("cin.txt","r",stdin);
while(~scanf("%d%d%d",&n,&m,&s))
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
map[i][j]=MaxInt;
}
}
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&p,&q,&t);
if(t<map[q][p])
map[q][p]=t;
}
dijkstra(s);
min1=MaxInt;
scanf("%d",&w);
while(w--)
{
scanf("%d",&ww);
if(min1>dist[ww]) min1=dist[ww];
}
if(min1!=MaxInt)
printf("%d\n",min1);
else printf("-1\n");
}
return 0;
}
5、限定必须连上给定的两点 这是一个关于最小生成树的原理应用,也是以签到题的姿态出现的区域赛题
HDU 4463 Outlets 最小生成树Kr~
既然要求他俩必须先连,那就先连上,逐渐选择最小的边加入边集中就ok啦 #include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=60*60;
struct point
{
int x,y;
double dis;
point(){}
point(int _x,int _y,double _d):x(_x),y(_y),dis(_d){}
bool operator <(const struct point &tmp)const{
return this->dis<tmp.dis;
}
}p[maxn];
point pp[100];
int par[60];
void init(){
for(int i=0;i<60;i++) par[i]=i;
}
int find_par(int x){
if(x!=par[x]) return par[x]=find_par(par[x]);
return par[x];
}
bool Union(int x,int y){
x=find_par(x);
y=find_par(y);
if(x!=y){
par[y]=x;
return true;
}
return false;
}
double calu(point a,point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main()
{
// freopen("cin.txt","r",stdin);
int n,p1,q1;
while(~scanf("%d",&n)&&n)
{
init();
scanf("%d%d",&p1,&q1);
int cnt=0;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&pp[i].x,&pp[i].y);
for(int j=1;j<i;j++)
{
double d=calu(pp[i],pp[j]);
p[cnt++]=point(i,j,d);
}
}
double ans=0;
Union(p1,q1);
ans+=calu(pp[p1],pp[q1]);
sort(p,p+cnt);
for(int i=0;i<cnt;i++)
{
if(Union(p[i].x,p[i].y))
ans+=p[i].dis;
}
printf("%.2lf\n",ans);
}
}
6、floyd最小环 裸题 前一个是输出路径 poj1734Sightseeing trip floyd最小环
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cstdio>
#include<climits>
#include<algorithm>
using namespace std;
const int N=111;
const int INF=0xffffff;
int min_loop;
int num;
int map[N][N],dist[N][N],pre[N][N];
int path[N];
int n,m;
void dfs(int i,int j)
{
int k=pre[i][j];
if(k==0)
{
path[num++]=j;
return;
}
dfs(i,k);
dfs(k,j);
}
void Floyd()
{
min_loop=INF;
memset(pre,0,sizeof(pre));
for(int k=1; k<=n; k++)
{
for(int i=1; i<k; i++)
{
for(int j=i+1; j<k; j++)
{
if(dist[i][j]+map[i][k]+map[k][j]<min_loop)
{
min_loop=dist[i][j]+map[i][k]+map[k][j];
num=0;
path[num++]=i;
dfs(i,j);
path[num++]=k;
}
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(dist[i][k]+dist[k][j]<dist[i][j])
{
dist[i][j]=dist[i][k]+dist[k][j];
pre[i][j]=k;
}
}
}
}
}
int main()
{
// freopen("cin.txt","r",stdin);
while(~scanf("%d%d",&n,&m))
{
for(int i=1; i<=n; i++)
{
for(int j=i+1; j<=n; j++)
map[i][j]=map[j][i]=dist[i][j]=dist[j][i]=INF;
map[i][i]=dist[i][i]=0;
}
for(int i=0; i<m; i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(w<map[u][v])
{
map[u][v]=map[v][u]=w;
dist[u][v]=dist[v][u]=w;
}
}
Floyd();
if(min_loop==INF)
puts("No solution.");
else
{
for(int i=0; i<num-1; i++) printf("%d ",path[i]);
printf("%d\n",path[num-1]);
//printf("%d\n",min_loop);
}
}
return 0;
}
7、单向最短路来回最短距离 虽说是来回都得求值,还记不记得之前的“孙大神的山峰序列” 一样的道理,从两边求完之后遍历数组求和最小即为答案
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cstdio>
using namespace std;
int n,m,x;
const int MAXINT=0x3f3f3f3f;
const int MAXNUM=1100;
int dist[MAXNUM];
int A[MAXNUM][MAXNUM];
void Dijkstra(int v0)
{
bool S[MAXNUM];// 判断是否已存入该点到S集合中
for(int i=1; i<=n; ++i)
{
dist[i]=A[v0][i];
// printf("%d ",dist[i]);
S[i]=false; // 初始都未用过该点
}
// dist[v0] = 0;
S[v0]=true;
for(int i=2; i<=n; i++)
{
int mindist = MAXINT;
int u = -1;// 找出当前未使用的点j的dist[j]最小值
for(int j=1; j<=n; ++j)
if((!S[j]) && dist[j]<mindist)
{
u = j;// u保存当前邻接点中距离最小的点的号码
mindist = dist[j];
//printf("%d ",mindist);
}
S[u] = true;
for(int j=1; j<=n; j++)
if((!S[j]) && A[u][j]<MAXINT)
{
if(dist[u] + A[u][j] < dist[j]) //在通过新加入的u点路径找到离v0点更短的路径
{
dist[j] = dist[u] + A[u][j]; //更新dis //记录前驱顶点
}
}
}
return;
}
int main()
{
//freopen("cin.txt","r",stdin);
while(~scanf("%d%d%d",&n,&m,&x))
{
for(int i=1; i<=n; i++)
{
for(int j=i+1; j<=n; j++)
A[i][j]=A[j][i]=MAXINT;
}
for(int i=0; i<m; i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
A[u][v]=w;
//printf("%d %d %d\n",u,v,A[u][v]);
}
Dijkstra(x);
int a1[MAXNUM],a2[MAXNUM];
for(int i=1;i<=n;i++) a1[i]=dist[i];//printf("%d ",a1[i]);
// puts("");
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
int tmp;
tmp=A[i][j];
A[i][j]=A[j][i];
A[j][i]=tmp;
}
Dijkstra(x);
for(int i=1;i<=n;i++) a2[i]=dist[i];
int minn=0;
for(int i=1;i<=n;i++)
{
minn=max(minn,a1[i]+a2[i]);
}
printf("%d\n",minn);
}
return 0;
}
8、换货币,给出汇率,问是否可以通过一系列的折腾使得手中的钱增多,其实也不容想到是用floyd最短路的方法写== dist[i][i]>1.0即为钱数增加
POJ 2240 Arbitrage floyd
#include <iostream>
#include <cstdio>
#include<map>
#include <string.h>
using namespace std;
map<string,int>mat;
double dist[100][100];
int n,m;
void Floyd()
{
for(int k=1;k<=n;k++)
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(dist[i][k]*dist[k][j]>dist[i][j])
{
dist[i][j]=dist[i][k]*dist[k][j];
// pre[i][j]=k;
}
}
}
}
int main()
{
// freopen("cin.txt","r",stdin);
map<string,int>mat;
int cnt=0;
double w;
string str,str1,str2;
while(~scanf("%d",&n)&&n)
{
//mat.clear();
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
{
if(i==j) dist[i][j]=1;
else dist[i][j]=0;
}
for(int i=1;i<=n;i++) cin>>str,mat[str]=i;
cin>>m;
while(m--)
{
cin>>str1>>w>>str2;
dist[mat[str1]][mat[str2]]=w;
//maap[mat[str1]][mat[str2]]=w;
}
Floyd();
printf("Case %d:",++cnt);
bool flag=false;
for(int i=1;i<=n;i++)
if(dist[i][i]>1.0)
{
flag=true;
break;
}
if(!flag) printf(" No\n");
else printf(" Yes\n");
}
return 0;
}
9、换货币,给出汇率和手续费,问是否可以使得手里钱增加 spfa
POJ 1860 Currency Exchange spfa
同样是换货币,之前的题是只有汇率,这个题还有手续费,spfa bellman都可做,我当时用的前者。为什么要用这两个方法,因为要判断"负环",当然了这个题存在“钱无限增加”的情况就说明松弛操作可以进行很多次,就是正常函数返回-1的情况==
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <string.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxm=511111;
const int maxn=111111;
struct EdgeNode
{
int to;
int next;
double rate,comm,w;
};
int cont[maxn];
EdgeNode edges[maxm];
int N,M,s;
double val;
int head[maxn],edge;
bool vis[maxn];
queue <int> que;
double dis[maxn];
void addedge(int u,int v,double r,double c)
{
edges[edge].comm=c,edges[edge].to=v;
edges[edge].next=head[u];edges[edge].w=0;
edges[edge].rate=r;head[u]=edge++;
}
void init()
{
memset(head,-1,sizeof(head));
edge=0;
memset(cont,0,sizeof(cont));
memset(vis,false,sizeof(vis));
for (int i=0; i<N; i++)
dis[i]=0;
}
int spfa(int s,int n)//单源最短路(s为起点,n为节点总数)///
{
int u;
while (!que.empty()) que.pop();
vis[s]=true;
dis[s]=val;
++cont[s];
que.push(s);
while (!que.empty())
{
u=que.front();
que.pop();
vis[u]=false;
for (int i=head[u]; i!=-1; i=edges[i].next)
{
int v=edges[i].to;
edges[i].w=(dis[u]-edges[i].comm)*edges[i].rate-dis[u];
if (dis[v]<dis[u]+edges[i].w)///
{
dis[v]=dis[u]+edges[i].w;
if (!vis[v])
{
vis[v]=true;
que.push(v);
}
if(++cont[v]>n)
return -1;
}
}
}
return 1;
}
int main()
{
// freopen("cin.txt","r",stdin);
while(~scanf("%d%d%d%lf",&N,&M,&s,&val))
{
int a,b;
init();
double rab,cab,rba,cba;
while(M--)
{
scanf("%d%d%lf%lf%lf%lf",&a,&b,&rab,&cab,&rba,&cba);
addedge(a,b,rab,cab);
addedge(b,a,rba,cba);
}
if(spfa(s,N)==-1)printf("YES\n");
else printf("NO\n");
}
return 0;
}
10、差分约束入门题 分糖果“小于等于” by the way 小于等于是最短路 大于等于是最长路
POJ 3159 Candies差分约束系统 spfa
核心在于:xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=30005;
int dis[maxn],head[maxn];
int n,m,sum;
bool vis[maxn];
struct node{
int v,w,next;
}edge[150010];
void addedge(int a,int b,int c){
edge[sum].v=b;
edge[sum].w=c;
edge[sum].next=head[a];
head[a]=sum++;
}
bool spfa(int s){
int stack[maxn],outque[maxn],top=0;
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(outque,0,sizeof(outque));
stack[top++]=s;
vis[s]=1;//vis数组的作用不是判断它是否被更新过 而是是否在栈里!
dis[s]=0;
while(top){
int tmp=stack[--top];
vis[tmp]=0;
outque[tmp]++;
if(outque[tmp]>n) return 0; //判断负环,当然这里没有必要写它
int k=head[tmp];
while(k>-1){
if(dis[edge[k].v]>edge[k].w+dis[tmp]){
dis[edge[k].v]=edge[k].w+dis[tmp];
if(vis[edge[k].v]==0){
vis[edge[k].v]=1;
stack[top++]=edge[k].v;
}
}
k=edge[k].next;
}
}
return 1;
}
int main()
{
//freopen("cin.txt","r",stdin);
while(cin>>n>>m){
sum=0;
memset(head,-1,sizeof(head));
int a,b,c;
while(m--){
scanf("%d%d%d",&a,&b,&c);
addedge(a,b,c);
}
spfa(1);
printf("%d\n",dis[n]);
}
return 0;
}
11、差分约束系统论文题
题意:每天每个时间有一定的收银员人数需求(以小时为单位)另有很多人应聘,一旦应聘,干满8小时,问最少雇佣多少人
s[]数组表示共雇佣了的人数,num[] 当前时刻最多可雇佣人数,r[]某时刻的最少人数。1. s[i]-s[i-1]>=0 2. s[i-1]-s[i]>=-num[i] (1<=i<=24) 3. s[i]-s[i-8]>=r[i](8<=i<=24) 4. s[i]-s[i+16]>=r[i]-ans (1<=i<=8) 5. s[24]-s[0]>=ans 注意第4个式子,是将第3个式子扩展到"每天"得到的
#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=50005;
struct Edge
{
int v,cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[maxn];
void addedge(int u,int v,int w)
{
E[u].push_back(Edge(v,w));
}
bool vis[maxn];
int cnt[maxn];
int dist[maxn];
bool spfa(int start,int n)
{
memset(vis,false,sizeof(vis));
memset(dist,-inf,sizeof(dist));
vis[start]=true;
dist[start]=0;
queue<int>que;
while(!que.empty()) que.pop();
que.push(start);
memset(cnt,0,sizeof(cnt));
cnt[start]=1;
while(!que.empty())
{
int u=que.front();
que.pop();
vis[u]=false;
for(int i=0;i<E[u].size();i++)
{
int v=E[u][i].v;
if(dist[v]<dist[u]+E[u][i].cost)
{
dist[v]=dist[u]+E[u][i].cost;
if(!vis[v])
{
vis[v]=true;
que.push(v);
if(++cnt[v]>n) return false;
}
}
}
}
return true;
}
int num[30],r[30];
int main()
{
//freopen("cin.txt","r",stdin);
int t,ans,m;
scanf("%d",&t);
while(t--)
{
for(int i=1;i<=24;i++) scanf("%d",&r[i]);
for(int i=0;i<25;i++) num[i]=0;
scanf("%d",&m);
for(int i=0;i<m;i++)
{
int tmp;
scanf("%d",&tmp);
num[tmp+1]++;///!!!
}
ans=1;
bool flag=true;
int l=0,right=m;
while(l<right)
{
for(int i=0;i<=24;i++) E[i].clear();
ans=(l+right)/2;
for(int i=1;i<=24;i++)
{
addedge(i-1,i,0);
addedge(i,i-1,-num[i]);
}
for(int i=8;i<=24;i++) addedge(i-8,i,r[i]);
for(int i=1;i<=8;i++) addedge(i+16,i,r[i]-ans);
addedge(0,24,ans);
if(spfa(0,25)) right=ans,flag=false;
else l=ans+1;
}
if(!flag) printf("%d\n",right);
else printf("No Solution\n");
}
return 0;
}
12、差分约束系统水题
hdu1531King
题意:给出asi+asi+1+…+asi+n大于或者小于ki 那么设从a[1]到a[i]的和为一个数,把它转化成最长路或者最短路都可做
#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=500;
struct Edge
{
int v,cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[maxn];
void addedge(int u,int v,int w)
{
E[u].push_back(Edge(v,w));
}
bool vis[maxn];
int cnt[maxn];
int dist[maxn];
bool spfa(int start,int n)
{
memset(vis,false,sizeof(vis));
memset(dist,-inf,sizeof(dist));
vis[start]=true;
dist[start]=0;
queue<int>que;
while(!que.empty()) que.pop();
que.push(start);
memset(cnt,0,sizeof(cnt));
cnt[start]=1;
while(!que.empty())
{
int u=que.front();
que.pop();
vis[u]=false;
for(int i=0;i<E[u].size();i++)
{
int v=E[u][i].v;
if(dist[v]<dist[u]+E[u][i].cost)
{
dist[v]=dist[u]+E[u][i].cost;
if(!vis[v])
{
vis[v]=true;
que.push(v);
if(++cnt[v]>n) return false;
}
}
}
}
return true;
}
int main()
{
// freopen("cin.txt","r",stdin);
int n,m,si,ni,ki;
char oi[4];
while(~scanf("%d",&n)&&n)
{
scanf("%d",&m);
for(int i=0;i<=n+2;i++) E[i].clear();
while(m--)
{
scanf("%d%d%s%d",&si,&ni,oi,&ki);
if(oi[0]=='g') addedge(si-1,si+ni,ki+1);
else addedge(si+ni,si-1,1-ki);
}
for(int i=0;i<=n;i++)addedge(n+1,i,0);
if(spfa(n+1,n+2)) puts("lamentable kingdom");
else puts("successful conspiracy");
}
return 0;
}
13、差分约束系统较难题 :跳房子
hdu3440House Man【差分约束系统】
题意:由左至右一堆高矮不同的小房子,只能从左向右水平距离不超过d个房子,问水平最多走多远做法:先为存有(点序号,房子高度)的结构体数组排序,枚举相邻高度的房子,为其加"d"的边,跑最短路。注意最终求的是最左边点(起点)和最右边点(终点)
#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1005;
struct Edge
{
int v,cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[maxn];
void addedge(int u,int v,int w)
{
E[u].push_back(Edge(v,w));
}
bool vis[maxn];
int cnt[maxn];
int dist[maxn];
bool spfa(int start,int n)
{
memset(vis,false,sizeof(vis));
memset(dist,inf,sizeof(dist));
vis[start]=true;
dist[start]=0;
queue<int>que;
while(!que.empty()) que.pop();
que.push(start);
memset(cnt,0,sizeof(cnt));
cnt[start]=1;
while(!que.empty())
{
int u=que.front();
que.pop();
vis[u]=false;
for(int i=0;i<E[u].size();i++)
{
int v=E[u][i].v;
if(dist[v]>dist[u]+E[u][i].cost)
{
dist[v]=dist[u]+E[u][i].cost;
if(!vis[v])
{
vis[v]=true;
que.push(v);
if(++cnt[v]>n) return false;
}
}
}
}
return true;
}
struct node
{
int val,pos;
}nt[maxn];
bool cmp(node a,node b)
{
return a.val<b.val;
}
int main()
{
// freopen("cin.txt","r",stdin);
int t,n,d,cas=1;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++) scanf("%d",&nt[i].val),nt[i].pos=i;
sort(nt+1,nt+1+n,cmp);
for(int i=0;i<=n;i++) E[i].clear();
for(int i=1;i<n;i++)
{
addedge(i+1,i,-1);
if(nt[i].pos<nt[i+1].pos)
addedge(nt[i].pos,nt[i+1].pos,d);
else addedge(nt[i+1].pos,nt[i].pos,d);
}
printf("Case %d: ",cas++);
int minn,maxn;
if(nt[1].pos<nt[n].pos)minn=nt[1].pos,maxn=nt[n].pos;
else minn=nt[n].pos,maxn=nt[1].pos;
if(!spfa(minn,n)) printf("-1\n");
else printf("%d\n",dist[maxn]);
}
return 0;
}
14、spfa最短路+tsp状压 题意:n*n的矩阵,每一个格子都有相应的花费,k个宝藏的位置。从任意边界进入任意边界出去。求出得到所有宝藏的最小花费。
思路:将边界作为0点。bfs求出宝藏之间以及宝藏与0点的最短距离。一次TSP,在图中跑一次回路,从起点0回到起点,得到最小花费。
附上的也是一个 13级妹子的代码,比我的那份写的好看太多 #include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int a[205][205], d[205][205];
int x[205], y[205], dp[1<<15][15];
bool v[205][205];
struct node {
int x, y, c;
node(int xx, int yy, int cc) {
x = xx; y = yy; c = cc;
}
bool operator <(const node & A) const {
return c > A.c;
}
};
int dx[]={1, -1, 0, 0}, dy[]={0, 0, 1, -1};
int n, m, k;
bool ok(int x, int y)
{
if(x < 0 || y < 0 || x >= n || y >= m || a[x][y] == -1) {
return false;
}
return true;
}
void bfs(int s, int t)
{
priority_queue<node> que;
memset(v, 0, sizeof(v));
que.push(node(x[s], y[s], 0));
v[x[s]][y[s]] = 1;
int c = 0;
while(!que.empty()) {
node e = que.top(); que.pop();
if(e.x == x[t] && e.y == y[t]) {
c = e.c - a[x[t]][y[t]];
}
for(int i = 0; i < 4; ++i) {
int nx = e.x+dx[i], ny = e.y+dy[i];
if(v[nx][ny] || a[nx][ny] == -1)
continue;
if(nx < 0 || ny < 0 || nx >= n || ny >= m) {
d[s][0] = min(d[s][0], e.c);
d[0][s] = min(d[0][s], e.c);
continue;
}
v[nx][ny] = 1;
que.push(node(nx, ny, e.c+a[nx][ny]));
}
}
if(s != t) {
d[s][t] = c;
d[t][s] = c;
}
}
void deal_p_p()
{
memset(d, 0x3f, sizeof(d));
for(int i = 1; i <= k; ++i) {
for(int j = 1; j <= k; ++j) {
//while(!que.empty()) que.pop();
bfs(i, j);
}
}
}
int solve()
{
memset(dp, 0x3f, sizeof(dp));
int nk = k+1;
dp[(1<<nk)-1][0] = 0;
for(int s = (1<<nk)-2; s >= 0; --s) {
for(int v = 0; v < nk; ++v) {
for(int u = 0; u < nk; ++u) {
if(!(s>>u &1)) {
dp[s][v] = min(dp[s][v], dp[s|1<<u][u]+d[v][u]);
}
}
}
}
return dp[0][0];
}
int main()
{
//freopen("in", "r", stdin);
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
memset(a, 0, sizeof(a));
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
scanf("%d", &a[i][j]);
}
}
int Sum = 0;
scanf("%d", &k);
memset(x, 0, sizeof(x));
memset(y, 0, sizeof(y));
for(int i = 1; i <= k; ++i) {
scanf("%d%d", &x[i], &y[i]);
Sum += a[x[i]][y[i]];
}
deal_p_p();
if(k == 1) {
if(d[1][0] == INF) {
printf("0\n");
}
else printf("%d\n", 2*d[1][0]+a[x[1]][y[1]]);
}
else {
int ans = solve();
if(ans == INF) printf("0\n");
else printf("%d\n", ans+Sum);
}
}
return 0;
}
15、取消一点使得最短路有最大值
hdu5137How Many Maos Does the Guanxi Worth
题意:暴发户给孩子办关系升学,需要一个人找一个人,人情钱都是这货出,你要劝说其中一个人不去帮忙使得暴发户花钱最多枚举所有点,逐次跑最短路即可
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=33;
const int inf=0x3f3f3f3f;
bool vis[maxn];
int pre[maxn],lowcost[maxn],cost[maxn][maxn];
void dijkstra(int n,int beg)
{
for(int i=0;i<n;i++)
{
lowcost[i]=inf;vis[i]=false;pre[i]=-1;
}
lowcost[beg]=0;
for(int j=0;j<n;j++)
{
int k=-1;
int Min=inf;
for(int i=0;i<n;i++)
if(!vis[i]&&lowcost[i]<Min)
{
Min=lowcost[i];
k=i;
}
if(k==-1)break;
vis[k]=true;
for(int i=0;i<n;i++)
if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i])
{
lowcost[i]=lowcost[k]+cost[k][i];
pre[i]=k;
}
}
}
int temp[maxn];
int main()
{
// freopen("cin.txt","r",stdin);
int n,m;
while(~scanf("%d%d",&n,&m)&&n&&m)
{
memset(cost,inf,sizeof(cost));
for(int i=0;i<m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
a--;b--;
if(c>cost[a][b]) continue;
cost[a][b]=cost[b][a]=c;
}
int ans=0;
for(int i=1;i<n-1;i++)
{
for(int j=0;j<=n-1;j++)
{
temp[j]=cost[i][j];
cost[i][j]=cost[j][i]=inf;
}
dijkstra(n,0);
if(lowcost[n-1]>ans) ans=lowcost[n-1];
for(int j=0;j<n;j++)
cost[i][j]=cost[j][i]=temp[j];
}
if(ans<inf) printf("%d\n",ans);
else printf("Inf\n");
}
return 0;
}
16、最短路反向建图(酋长是终点不是起点,最开始所有点前面再加一个源点)枚举等级区间依次跑最短路即可
poj1062昂贵的聘礼
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=133;
const int inf=0x3f3f3f3f;
bool vis[maxn];
int pre[maxn],lowcost[maxn],cost[maxn][maxn],val[maxn],rank[maxn];
void dijkstra(int cost[][maxn],int n,int beg)
{
for(int i=0;i<n;i++)
{
lowcost[i]=inf;vis[i]=false;pre[i]=-1;
}
lowcost[beg]=0;
for(int j=0;j<n;j++)
{
int k=-1;
int Min=inf;
for(int i=0;i<n;i++)
if(!vis[i]&&lowcost[i]<Min)
{
Min=lowcost[i];
k=i;
}
if(k==-1)break;
vis[k]=true;
for(int i=0;i<n;i++)
if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i])
{
lowcost[i]=lowcost[k]+cost[k][i];
pre[i]=k;
}
}
}
int tmp[maxn][maxn];
int main()
{
freopen("cin.txt","r",stdin);
int m,n;
while(~scanf("%d%d",&m,&n))
{
memset(cost,inf,sizeof(cost));
int maxn=0,minn=inf;
for(int i=0;i<n;i++)
{
int x;
scanf("%d%d%d",&cost[n][i],&rank[i],&x);
if(rank[i]>maxn)maxn=rank[i];
if(rank[i]<minn)minn=rank[i];
while(x--)
{
int t,v;
scanf("%d%d",&t,&v);t--;
if(v<cost[i][t]) cost[i][t]=v;
}
}
int ans=inf;
for(int k=max(0,rank[0]-m);k<=rank[0]+m;k++)
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(rank[i]>=k&&rank[i]<=k+m&&rank[j]>=k&&rank[j]<=k+m)
tmp[i][j]=cost[i][j];
else tmp[i][j]=inf;
}
dijkstra(tmp,n,0);
for(int i=0;i<n;i++)
if(ans>lowcost[i]+cost[n][i])
ans=lowcost[i]+cost[n][i];
}
printf("%d\n",ans);
}
return 0;
}
17、送餐来回的最短距离
看到这个题的时候想到上面的第7题了,然而第7题不需要每个点都遍历一遍,而这个题需要,所以这个是需要用状压的tsp的
poj3311 Hie with the Pie【floyd最短路+状态压缩】
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[1<<11][20],n,num[11][11];
int min(int a,int b){if(a<b)return a;return b;}
int judge(int s)
{
int num=0;
while(s)
{
if(s&1)num++;
s>>=1;
}
return num;
}
int dist[11][11];
void init()
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
dist[i][j]=num[i][j];
}
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
for(int k=0;k<n;k++)
if(dist[j][i]+dist[i][k]<dist[j][k])
dist[j][k]=dist[j][i]+dist[i][k];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++);
// printf("i=%d,j=%d,dist=%d\n",i,j,dist[i][j]);
}
int main()
{
// freopen("cin.txt","r",stdin);
while(~scanf("%d",&n)&&n)
{
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
scanf("%d",&num[i][j]);
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[0][0]=0;
n++;//have n+1 points
init();
for(int i=0;i<n;i++)//n points
dp[1<<i|1][i]=dist[0][i];
// for(int i=0;i<(1<<n)-1;i++) if(dp[i]!=0x3f3f3f3f)printf("i=%d dp=%d ",i,dp[i]);
int ans=0x3f3f3f3f;
for(int i=1;i<(1<<n)-1;i++)//now
{
for(int j=0;j<n;j++)//not exist
{
if(i&(1<<j)) continue;
for(int k=0;k<n;k++)//exist
{
if(j==k) continue;
if(i&(1<<k))
{
dp[1<<j|i][j]=min(dp[i][k]+dist[k][j],dp[1<<j|i][j]);
if(judge(1<<j|i)==n)
ans=min(ans,dp[1<<j|i][j]+dist[j][0]);
}
}
}
}
printf("%d\n",ans);
}
return 0;
}
18、 建全国的路的一部分,依旧要求联通,但是首都到各个城市的最短距离不变的前提下求最小花费
Aizu 2249Road Construction 单源最短路变形 spfa模板改写
既然是首都到各地的距离最短前提下求最小花费,那么松弛操作当距离相等的时候压入花费小的就可以啦 #include <stdio.h>
#include <string.h>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=10050;
const int maxe=2005000;
const int INF=1e9;
struct note
{
int to;
int w;
int c;
int next;
};
note edge[maxe];
int head[maxn];
int ip;
void init()
{
memset(head,-1,sizeof(head));
ip=0;
}
void addedge(int u,int v,int w,int c)
{
edge[ip].to=v;
edge[ip].c=c;
edge[ip].w=w;
edge[ip].next=head[u];
head[u]=ip++;
}
int cost[maxn];
int dis[maxn];
bool vis[maxn];
queue<int>q;
void spfa(int s,int n)
{
while(!q.empty())q.pop();
for(int i=1;i<=n;i++)
cost[i]=dis[i]=INF;
memset(vis,false,sizeof(vis));
dis[s]=0;
cost[s]=0;
vis[s]=true;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
int val=edge[i].w;
if(dis[to]>dis[u]+val||(dis[to]==dis[u]+val&&cost[to]>edge[i].c))//有多条最短路时,取花费最小的
{
dis[to]=dis[u]+val;
cost[to]=edge[i].c;
if(!vis[to])
{
q.push(to);
vis[to]=true;
}
}
}
}
}
int main()
{
// freopen("cin.txt","r",stdin);
int n,m;
int u,v,w,c;
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
init();
for(int i=0;i<m;i++)
{
scanf("%d%d%d%d",&u,&v,&c,&w);
addedge(u,v,c,w);
addedge(v,u,c,w);
}
spfa(1,n);
int ans=0;
for(int i=1;i<=n;i++)
ans+=cost[i];
printf("%d\n",ans);
}
return 0;
}
19、 差分约束系统
题意:对于给定有向图,定义一种操作:对于选定的某一点,进入这个点的所有边权都减去d,从这个点出去的所有边权都加上d。经过一系列的操作,使得所有边权的最小值达到最大,需保证这个最值是正数
by the way Spfa要是最开始不加超级源点,就要把所有的点都压入队列
这么看来就和上面的题一样啦
#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f;
const int maxn=505;
int n,m;
struct Edge
{
int v,cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[maxn];
void addedge(int u,int v,int w)
{
E[u].push_back(Edge(v,w));
}
bool vis[maxn];
int cnt[maxn];
int dist[maxn];
bool spfa(int start,int n)
{
memset(vis,false,sizeof(vis));
memset(dist,inf,sizeof(dist));
vis[start]=true;
dist[start]=0;
queue<int>que;
while(!que.empty()) que.pop();
que.push(start);
memset(cnt,0,sizeof(cnt));
cnt[start]=1;
while(!que.empty())
{
int u=que.front();
que.pop();
vis[u]=false;
for(int i=0;i<E[u].size();i++)
{
int v=E[u][i].v;
if(dist[v]>dist[u]+E[u][i].cost)
{
dist[v]=dist[u]+E[u][i].cost;
if(!vis[v])
{
vis[v]=true;
que.push(v);
if(++cnt[v]>n) return false;
}
}
}
}
return true;
}
bool judge(int x)
{
// cout<<"rrr"<<endl;
for(int i=1;i<=n;i++)
{
for(int j=0;j<E[i].size();j++)
E[i][j].cost-=x;
}
bool flag=spfa(0,n);
for(int i=1;i<=n;i++)
{
for(int j=0;j<E[i].size();j++)
E[i][j].cost+=x;
}
return flag;
}
int main()
{
//freopen("cin.txt","r",stdin);
// int cas=1;
// scanf("%d",&t);
while(~scanf("%d%d",&n,&m))
{
// printf("Case #%d: ",cas++);
for(int i=0;i<=n;i++) E[i].clear();
// memset(head,-1,sizeof(head));
int l=1,mid,r=-inf;
while(m--)
{
int a,b;
int c;
scanf("%d%d%d",&a,&b,&c);
addedge(a,b,c);
if(c>r)r=c;
}
for(int i=1;i<=n;i++)addedge(0,i,0);
// cout<<"rrr"<<endl;
// printf("r=%d\n",r);
if(judge(r))
{
printf("Infinite\n");
continue;
}
else if(!judge(1))
{
puts("No Solution");
continue;
}
int ans;
while(l<=r)
{
mid=(l+r)/2;
// cout<<mid<<endl;
// printf("l=%d,mid=%d,r=%d ",l,mid,r);
if(judge(mid)) ans=mid,l=mid+1;
else {r=mid-1;}
// printf("l=%d,mid=%d,r=%d,ans=%d\n",l,mid,r,ans);
}
printf("%d\n",ans);
}
return 0;
}
20、二分找负环
一样的题:给定有向带权图,求最小环的值。二分找最小值,边权都减去这个数
#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const double inf=0x3f3f3f3f;
const int maxn=100;
int n,m,t;
double dist[maxn];
struct Edge
{
int u,v;
double cost;
Edge(int _u=0,int _v=0,double _cost=0):u(_u),v(_v),cost(_cost){}
};
vector<Edge>E;
bool bellman(int start,int n)
{
for(int i=1;i<=n;i++)dist[i]=inf;
dist[start]=0;
for(int i=1;i<n;i++)
{
bool flag=false;
for(int j=0;j<E.size();j++)
{
int u=E[j].u;
int v=E[j].v;
double cost=E[j].cost;
if(dist[v]>dist[u]+cost)
{
dist[v]=dist[u]+cost;
flag=true;
}
}
if(!flag)return true;
}
for(int j=0;j<E.size();j++)
if(dist[E[j].v]>dist[E[j].u]+E[j].cost)
return false;
return true;
}
bool judge(double x)
{
for(int i=0;i<E.size();i++) E[i].cost-=x;
bool flag=bellman(1,n);
for(int i=0;i<E.size();i++) E[i].cost+=x;
return flag;
}
int main()
{
//freopen("cin.txt","r",stdin);
int cas=1;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
printf("Case #%d: ",cas++);
E.clear();
double r=0.0;
while(m--)
{
int a,b;
double c;
scanf("%d%d%lf",&a,&b,&c);
E.push_back(Edge(a,b,c));
if(c>r)r=c;
}
// printf("r=%lf\n",r);
if(judge(r+1))
{
printf("No cycle found.\n");
continue;
}
double l=0.0,mid;
while(r-l>=0.0001)
{
mid=(l+r)/2;
for(int i=0;i<E.size();i++) E[i].cost-=mid;
if(bellman(1,n)) l=mid;
else r=mid;
for(int i=0;i<E.size();i++) E[i].cost+=mid;
}
printf("%.2lf\n",r);
}
return 0;
}
21、
hdu4786Fibonacci Tree【kruskal最小生成树】
给定无向图每个边要么是白色,要么是黑色。求生成树的白边个数能否是斐波那契数。其实应该能想到的,让白边是1,黑边是0,求最大生成树和最小生成树。看中间范围是否有斐波那契数。 #include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200005;
const int maxm=200005;
int F[maxn];
struct Edge
{
int u,v,w;
}edge[maxm];
int tol;
void addedge(int u,int v,int w)
{
edge[tol].v=v;
edge[tol].u=u;
edge[tol++].w=w;
}
bool cmp(Edge a,Edge b)
{
return a.w<b.w;
}
bool cmp2(Edge a,Edge b)
{
return a.w>b.w;
}
int fnd(int x)
{
return x==F[x]?x:F[x]=fnd(F[x]);
}
int kruskal(int n)
{
for(int i=1;i<=n;i++) F[i]=i;
// sort(edge,edge+tol,cmp);
int cnt=0;
int ans=0;
for(int i=0;i<tol;i++)
{
int u=edge[i].u;
int v=edge[i].v;
int w=edge[i].w;
int a=fnd(u);
int b=fnd(v);
if(a!=b)
{
ans+=w;
F[b]=a;
cnt++;
}
if(cnt==n-1)break;
}
if(cnt<n-1)return -1;
return ans;
}
int num[100],cnt;
void init()
{
num[0]=0;num[1]=1;num[2]=2;num[3]=3;
cnt=4;
while(num[cnt-1]<=100005)
{
num[cnt]=num[cnt-1]+num[cnt-2];
cnt++;
// cout<<num[cnt-1]<<endl;
}
}
int main()
{
//freopen("cin.txt","r",stdin);
int t,n,m,cas=1;
scanf("%d",&t);
init();
while(t--)
{
scanf("%d%d",&n,&m);
tol=0;
while(m--)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);///!!!
}
printf("Case #%d: ",cas++);
sort(edge,edge+tol,cmp);
int small=kruskal(n);
bool ff=true;
sort(edge,edge+tol,cmp2);
int big=kruskal(n);
// printf("s=%d,b=%d\n",small,big);
if(small==-1||big==-1)
{
puts("No");
continue;
}
bool flag=false;
for(int i=1;i<=23;i++)
{
if(num[i]>=small&&num[i]<=big)
{
flag=true;
break;
}
}
if(flag) printf("Yes\n");
else printf("No\n");
}
return 0;
}
22、2015多校第一场(盗墓笔记)
hdu5294Tricks Device【最短路+网络流】
从1开始到n结束,给出双向道路,问至少去掉多少边最短路不是原始最短路长度,至多去掉多少条边最短路长度不变分别利用1与n作为源点 ,分别跑一遍最短路,这个时候两个dist数组记录的就是以1、n为源点的单源最短路长度,理论上他俩无缝接起来都是最短路的方案(理论上是,实际上也是:P )那么我就依次验证给定的边长是否在最短路上,在的话,给这对点建网络流的边(这个题最最讨厌的地方在于函数名,数组名都差不多,特别容易写错)流量是题中给定的第三个数组,求完最短路还要给这对点再建一遍最短路的边,边长为一,这样跑下来dist[n]就是最短路的最短长度了
#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f;
const int maxn=2111;
int n,m;
struct Edge
{
int v,cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[maxn];
void addedge(int u,int v,int w)
{
E[u].push_back(Edge(v,w));
}
bool vis[maxn];
int cnt[maxn];
int dist[maxn],dist1[maxn];
bool spfa(int start,int n)
{
memset(vis,false,sizeof(vis));
memset(dist,inf,sizeof(dist));
vis[start]=true;
dist[start]=0;
queue<int>que;
while(!que.empty()) que.pop();
que.push(start);
memset(cnt,0,sizeof(cnt));
cnt[start]=1;
while(!que.empty())
{
int u=que.front();
que.pop();
vis[u]=false;
for(int i=0;i<E[u].size();i++)
{
int v=E[u][i].v;
if(dist[v]>dist[u]+E[u][i].cost)
{
dist[v]=dist[u]+E[u][i].cost;
if(!vis[v])
{
vis[v]=true;
que.push(v);
if(++cnt[v]>n) return false;
}
}
}
}
return true;
}
int w[60009],u[60009],v[60009];
////////////////////////////////////////
const int mm=161111;
const int mn=600009;
const int oo=1000000000;
int node,src,dest,edge;
int reach[mm],flow[mm],nxt[mm];
int head[mn],work[mn],dis[mn],q[mn];
inline int min(int a,int b)
{
return a<b?a:b;
}
inline void prepare(int _node,int _src,int _dest)
{
node=_node,src=_src,dest=_dest;
for(int i=0;i<node;++i)head[i]=-1;
edge=0;
}
inline void addedge(int u,int v,int c1,int c2)
{
reach[edge]=v,flow[edge]=c1,nxt[edge]=head[u],head[u]=edge++;
reach[edge]=u,flow[edge]=c2,nxt[edge]=head[v],head[v]=edge++;
}
bool Dinic_bfs()
{
int i,u,v,l,r=0;
for(i=0;i<node;++i)dis[i]=-1;
dis[q[r++]=src]=0;
for(l=0;l<r;++l)
for(i=head[u=q[l]];i>=0;i=nxt[i])
if(flow[i]&&dis[v=reach[i]]<0)
{
dis[q[r++]=v]=dis[u]+1;
if(v==dest)return 1;
}
return 0;
}
int Dinic_dfs(int u,int exp)
{
if(u==dest)return exp;
for(int &i=work[u],v,tmp;i>=0;i=nxt[i])
if(flow[i]&&dis[v=reach[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0)
{
flow[i]-=tmp;
flow[i^1]+=tmp;
return tmp;
}dis[u]--;
return 0;
}
int Dinic_flow()
{
int i,ret=0,delta;
while(Dinic_bfs())
{
for(i=0;i<node;++i)work[i]=head[i];
while(delta=Dinic_dfs(src,oo))ret+=delta;
}
return ret;
}
int bb[mn][2],dist2[mn];
int main()
{
// freopen("cin.txt","r",stdin);
while(~scanf("%d%d",&n,&m))
{
for(int i=0;i<=n;i++) E[i].clear();
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u[i],&v[i],&w[i]);
addedge(u[i],v[i],w[i]);
addedge(v[i],u[i],w[i]);
}
spfa(1,n);
memcpy(dist2,dist,sizeof(dist));
spfa(n,n);
int k=0;
for(int i=0;i<m;i++)
{
if(dist2[u[i]]>dist2[v[i]])swap(u[i],v[i]);
if(dist[v[i]]+w[i]+dist2[u[i]]==dist2[n])
{
bb[k][0]=u[i];
bb[k++][1]=v[i];
}
}
prepare(n+1,1,n);
for(int i=0;i<k;i++)
{
addedge(bb[i][0],bb[i][1],1,0);
// addedge(bb[i][1],bb[i][0],1);
}
int x=Dinic_flow();
for(int i=0;i<=n;i++) E[i].clear();
for(int i=0;i<k;i++)
{
addedge(bb[i][0],bb[i][1],1);
addedge(bb[i][1],bb[i][0],1);
}
spfa(1,n);
printf("%d %d\n",x,m-dist[n]);
}
return 0;
}
23、次小生成树(秦始皇)
说题意:秦始皇统一中国之后要在全国修公路连接各个城市,抠门皇帝只想修成最小生成树(距离最小,不考虑人力),一个道士说自己可以不花人力物力修一条路,经过两方妥协,选择max(两个城市人口/生成树长度-这条路的长度)的路让他变,求这个比值最大值。
这个题没问次小生成树的值,但是用到了路径最长边这个数组,我叫他Max[][],题中问的是(两个城市人口/生成树长度-这条路的长度),我们首先考虑分母最小,因为对于某个边来说分子是定值啊,分母分成两种情况:1.这个边是最小生成树上的,那啥也别说,mst-cost[][]即可 2.这条边不在最小生成树上:那就是含有这个边的某个生成树,(这么说来这个题应该归纳到次小生成树啊==),mst-Max+cost即可
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=1010;
const int inf=0x3f3f3f3f;
bool vis[maxn],used[maxn][maxn];
double lowc[maxn];
int pre[maxn];
double Max[maxn][maxn],cost[maxn][maxn];
struct node
{
int x,y,pop;
}num[1010];
double min(double a,double b){if(a<b) return a;return b;}
double max(double a,double b){if(a>b) return a;return b;}
inline double Dist(node v1,node v2)
{
return sqrt(double(v1.x-v2.x)*(v1.x-v2.x)+double(v1.y-v2.y)*(v1.y-v2.y));
}
double prim(int n)
{
double ans=0;
memset(vis,false,sizeof(vis));
memset(Max,0,sizeof(Max));
memset(used,false,sizeof(used));
vis[0]=true;
pre[0]=-1;
for(int i=0;i<n;i++)
{
lowc[i]=cost[0][i];
pre[i]=0;
}
// lowc[0]=0.0;
for(int i=1;i<n;i++)
{
double minc=1.0*inf;
int p=-1;
for(int j=0;j<n;j++)
if(!vis[j]&&(minc>lowc[j]||p==-1))
{
minc=lowc[j];
p=j;
}
ans+=minc;
vis[p]=true;
used[p][pre[p]]=used[pre[p]][p]=true;
for(int j=0;j<n;j++)
{
if(vis[j]&&j!=p)//j!=p必须加!!1
Max[j][p]=Max[p][j]=max(Max[j][pre[p]],lowc[p]);
if(!vis[j]&&lowc[j]>cost[p][j])
{
lowc[j]=cost[p][j];
pre[j]=p;
}
}
}
return ans;
}
int main()
{
//freopen("cin.txt","r",stdin);
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d%d%d",&num[i].x,&num[i].y,&num[i].pop);
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
cost[i][j]=cost[j][i]=Dist(num[i],num[j]);
double mst=prim(n);
// double mst=smst(n,ans);
double ans=-1;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
{
if(used[i][j])//on mst
ans=max(ans,1.0*(num[i].pop+num[j].pop)/(mst-cost[i][j]));
else
ans=max(ans,1.0*(num[i].pop+num[j].pop)/(mst-Max[i][j]));
}
printf("%.2f\n",ans);
}
return 0;
}