比赛地址
密码:acm2016
A - 畅通工程 (HDU1863)
题目链接
【题意】
问题意中给出的图是否能求出一颗最小生成树,若不能求出输出?否则输出最小生成树权值。
【分析】
简单的最小生成树模板题。
【Code】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define Mx 1000+10
int cost[Mx][Mx],dis[Mx];
bool vis[Mx];
int n,m;
int Prim(int s)
{
int sum=0;
for (int i=1; i<=n; i++) dis[i]=cost[s][i];
memset(vis,0,sizeof(vis));
vis[s]=true;
for (int i=1; i<n; i++)
{
int mi=INF,k;
for (int j=1; j<=n; j++)
if(!vis[j]&&dis[j]<mi)
{
mi=dis[j];
k=j;
}
if (mi>=INF) {
return -1;
}
sum+=mi;
vis[k]=true;
for(int j=1; j<=n; j++)
if(!vis[j]&&cost[k][j]<dis[j])
dis[j]=cost[k][j];
}
return sum;
}
int main()
{
int x,y,z;
while(scanf("%d%d",&m,&n)!=EOF && m)
{
memset(cost,INF,sizeof(cost));
for(int i=1; i<=m; i++){
scanf("%d%d%d",&x,&y,&z);
cost[x][y] = cost[y][x] = z;
}
int ans = Prim(1);
if (ans == -1) puts("?");
else printf("%d\n",ans);
}
return 0;
}
B - 畅通工程再续 (HDU1875)
题目链接
【题意】
给出一个图,任意两个点之间有≥10,≤1000的边即可视为有边相连,边权为两点的距离,求图的最小生成树,若不存在输出”oh!”
【分析】
建图求最小生成树,模板题。
【Code】
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define Mx 1000+10
double cost[Mx][Mx],dis[Mx];
bool vis[Mx];
int n,m;
struct node{
double x,y;
} p[Mx];
double Prim(int s)
{
double sum=0;
for (int i=1; i<=n; i++) dis[i]=cost[s][i];
memset(vis,0,sizeof(vis));
vis[s]=true;
for (int i=1; i<n; i++)
{
double mi=INF;
int k;
for (int j=1; j<=n; j++)
if(!vis[j]&&dis[j]<mi)
{
mi=dis[j];
k=j;
}
if (mi+1e-9>=INF) {
return -1.0;
}
sum+=mi;
vis[k]=true;
for(int j=1; j<=n; j++)
if(!vis[j]&&cost[k][j]<dis[j])
dis[j]=cost[k][j];
}
return sum;
}
double dist(int i,int j)
{
return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x) + (p[i].y-p[j].y)*(p[i].y-p[j].y));
}
int main()
{
int Case;
scanf("%d",&Case);
while(Case--)
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++){
double d = dist(i,j);
if (d<10||d>1000) {
cost[j][i] = cost[i][j] = INF;
continue;
}
cost[i][j] = cost[j][i] = d;
}
double ans = Prim(1);
if (ans<0) puts("oh!");
else printf("%.1f\n",ans*100);
}
return 0;
}
C - Highways (POJ2485)
题目链接
【题意】
给定一个图,求该图最小生成树中的最长边。
【分析】
模板题,不多说。
【Code】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define Mx 1000+10
int cost[Mx][Mx],dis[Mx];
bool vis[Mx];
int n;
int Prim(int s)
{
int ans=0;
for (int i=1; i<=n; i++) dis[i]=cost[s][i];
memset(vis,0,sizeof(vis));
vis[s]=true;
for (int i=1; i<n; i++)
{
int mi=INF,k;
for (int j=1; j<=n; j++)
if(!vis[j]&&dis[j]<mi)
{
mi=dis[j];
k=j;
}
ans = max(ans,mi);
vis[k]=true;
for(int j=1; j<=n; j++)
if(!vis[j]&&cost[k][j]<dis[j])
dis[j]=cost[k][j];
}
return ans;
}
int main()
{
int Case;
scanf("%d",&Case);
while(Case--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
scanf("%d",&cost[i][j]);
printf("%d\n",Prim(1));
}
return 0;
}
D - 确定比赛名次 (POJ1285)
题目链接
【题意】
求满足条件的队伍顺序,符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前
【分析】
拓扑排序+优先队列,优先编号小的即可。
顺序输出即可。
【Code】
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
const int MAX_N = 500 + 10;
const int MAX_M = 10000 + 10;
const double PI = acos(-1.0);
const double EPS = 1e-9;
vector<int> vec[MAX_N];
int d[MAX_N],p[MAX_N];
bool v[MAX_N];
int n,m,cnt;
struct cmp{
bool operator()(int &a, int &b){
return a > b;
}
};
void toposort()
{
priority_queue<int,vector<int>,cmp> pq;
for (int i=1;i<=n;i++)
if (d[i] == 0)
pq.push(i);
while (!pq.empty()){
int x = pq.top();
pq.pop();
p[++cnt] = x;
for (int i=0;i<vec[x].size();i++){
int u = vec[x][i];
d[u]--;
if (d[u]==0) pq.push(u);
}
}
}
int main()
{
while (~scanf("%d%d",&n,&m)){
for (int i=0;i<=n;i++) vec[i].clear();
memset(v,0,sizeof(v));
memset(d,0,sizeof(d));
int a, b;
for (int i=0;i<m;i++){
scanf("%d%d",&a,&b);
d[b]++;
vec[a].push_back(b);
}
cnt = 0;
toposort();
for (int i=1;i<n;i++) printf("%d ",p[i]);
printf("%d\n",p[n]);
}
}
E - Test for Job (POJ3249)
题目链接
【题意】
求图中的一条路径,该路径的点权值和最大。
图可能不联通。
【分析】
拓扑排序+DP。
拓扑排序按节点度数入队,保证更新过的点不会再次被更新,即没有后效性,然后就可以DP了。
f[i]代表到i点的最大的点权值和,那么f[i] = max(f[i],f[x] + a[i]).
f[x]为可以到该点的点。
【Code】
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
const int MAX_N = 100000 + 10;
const int MAX_M = 1000000 + 10;
const double PI = acos(-1.0);
const double EPS = 1e-9;
vector<int> vec[MAX_N];
int d[MAX_N],p[MAX_N],f[MAX_N],a[MAX_N];
bool v[MAX_N];
int n,m,cnt;
void toposort()
{
// priority_queue<int,vector<int>,greater<int> > pq;
queue<int> pq;
for (int i=1;i<=n;i++)
if (d[i] == 0) {
pq.push(i);
f[i] = a[i];
}
while (!pq.empty()){
int x = pq.front();//pq.top();
pq.pop();
for (int i=0;i<vec[x].size();i++){
int u = vec[x][i];
f[u] = max(f[u],f[x]+a[u]);
//printf("%d %d %d\n",x,u,f[u]);
d[u]--;
if (d[u]==0) pq.push(u);
}
}
}
int main()
{
while (~scanf("%d%d",&n,&m)){
for (int i=0;i<=n;i++) vec[i].clear();
memset(d,0,sizeof(d));
memset(f,-INF,sizeof(f));
int c, b;
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=0;i<m;i++){
scanf("%d%d",&c,&b);
d[b]++;
vec[c].push_back(b);
}
cnt = 0;
toposort();
int ans = -INF;
for (int i=1;i<=n;i++)
if (!vec[i].size()){
ans = max(ans,f[i]);
}
printf("%d\n",ans);
}
}
F - 湫湫系列故事——设计风景线 (HDU4514)
题目链接
【题意】
问给定的图是否能构成环,如果能构成输出YES,否则输出该图中最长的一条边。
【分析】
首先用并查集判断该图是否能构成环,能就直接输出YES。
否则的话对于每个联通块,求该联通块中的最长边,最后取max即可。
求最长边的过程就是从该联通块中任意一点出发,求它到其他点的最长距离,记录这个点,然后从这个点出发,求该联通块中这个点到其他点的最长距离。
至于为什么这样就能得出最长边,是很显而易见的,就不多解释了。
【Code】
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const double EPS = 1e-9;
const int MAX_N = 100000 + 10;
const int MAX_M = 1000000 + 10;
struct node{
int from,to,v,next;
} p[MAX_M];
int head[MAX_N],d[MAX_N],f[MAX_N];
bool v[MAX_N],vis[MAX_N];
int n,m,cnt,k,tmp,ans;
void add(int u,int v,int w)
{
p[cnt].from= u;
p[cnt].to= v;
p[cnt].v= w;
p[cnt].next= head[u];
head[u]= cnt++;
}
int findx(int x)
{
if (f[x] == x) return x;
return f[x] = findx(f[x]);
}
void unite(int x,int y)
{
x = findx(x);
y = findx(y);
f[y] = x;
}
void dfs(int now,int cost)
{
if (cost > tmp){
tmp =cost;
k = now;
}
v[now] = true;
vis[now] = true;
for (int i=head[now];i!=-1;i=p[i].next){
int t = p[i].to;
if (!vis[t]){
dfs(t,cost+p[i].v);
}
}
}
int main()
{
int x, y, z;
while (~scanf("%d%d",&n,&m)){
for (int i=1;i<=n;i++) f[i] = i;
bool flag = false;
cnt = 0;
memset(head,-1,sizeof(head));
for (int i=0;i<m;i++){
scanf("%d%d%d",&x,&y,&z);
if (flag) continue;
int xx = findx(x), yy = findx(y);
if (xx == yy) flag = true;//一开始直接 x = findx(x),y = findx(y)导致后面建边出错,GG
else unite(x,y);
add(x,y,z);
add(y,x,z);
}
if (flag) printf("YES\n");
else {
ans = -1;
memset(v,0,sizeof(v));
for (int i=1;i<=n;i++){
if (!v[i]){
tmp = -1;
memset(vis,0,sizeof(vis));
dfs(i,0);
ans = max(tmp,ans);
memset(vis,0,sizeof(vis));
tmp = -1;
dfs(k,0);
ans = max(tmp,ans);
}
}
printf("%d\n",ans);
}
}
}
G - Til the Cows Come Home (POJ2387)
题目链接
【题意】
求1到N的最短路
【分析】
裸的最短路,Dijkstra 或 Spfa都可以。
【Code】
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const double EPS = 1e-9;
const int MAX_N = 10000;
struct node{
int from,to,v,next;
} p[MAX_N];
int head[MAX_N],d[MAX_N];
bool v[MAX_N];
int n,m,cnt = 0;
void add(int u,int v,int w)
{
p[cnt].from= u;
p[cnt].to= v;
p[cnt].v= w;
p[cnt].next= head[u];
head[u]= cnt++;
}
void spfa(int s)
{
for(int i=0;i<MAX_N;i++) d[i]=INF;
memset(v,false,sizeof(v));
queue<int> q;
d[s]=0;v[s]=true;
q.push(s);
while (!q.empty())
{
int u=q.front();
q.pop();
v[u]=true;
for (int i=head[u];i!=-1;i=p[i].next)
{
int t=p[i].to;
if (d[t]>d[u]+p[i].v)
{
d[t]=d[u]+p[i].v;
if (!v[t])
{
v[t]=true;
q.push(t);
}
}
}
v[u]=false;
}
}
int main()
{
int x,y,z;
scanf("%d%d",&m,&n);
cnt=0;
memset(head, -1, sizeof(head));
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
spfa(1);
printf("%d\n",d[n]);
}
H - 最短路径问题 (HDU3790)
题目链接
【题意】
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
【分析】
模板题,判断条件改一下就好。
【Code】
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const double EPS = 1e-9;
const int MAX_N = 400000 + 10;
struct node{
int from,to,v,w,next;
} p[MAX_N];
int head[MAX_N],d[MAX_N],c[MAX_N];
bool v[MAX_N];
int n, m, cnt = 0;
void add(int u,int v,int w,int x)
{
p[cnt].from= u;
p[cnt].to= v;
p[cnt].v= w;
p[cnt].w = x;
p[cnt].next= head[u];
head[u]= cnt++;
}
void spfa(int s)
{
for (int i=0;i<=n;i++) d[i]=INF;
for (int i=0;i<=n;i++) c[i]=INF;
memset(v,false,sizeof(v));
queue<int> q;
d[s]=0;c[s] = 0;
v[s]=true;
q.push(s);
while (!q.empty())
{
int u=q.front();
q.pop();
v[u]=true;
for (int i=head[u];i!=-1;i=p[i].next){
int t = p[i].to;
if (d[t] > d[u] + p[i].v){
d[t] = d[u] + p[i].v;
c[t] = c[u] + p[i].w;
if (!v[t]){
v[t]=true;
q.push(t);
}
}else if (d[t] == d[u] + p[i].v){
if (c[t] > c[u] + p[i].w){
c[t] = c[u] + p[i].w;
if (!v[t]){
v[t] = true;
q.push(t);
}
}
}
}
v[u]=false;
}
}
int main()
{
int x,y,z,w;
while (~scanf("%d%d",&n,&m)&&(m||n)){
cnt=0;
memset(head, -1, sizeof(head));
for (int i=1;i<=m;i++){
scanf("%d%d%d%d",&x,&y,&z,&w);
add(x,y,z,w);
add(y,x,z,w);
}
int s,t;
scanf("%d%d",&s,&t);
spfa(s);
printf("%d %d\n",d[t],c[t]);
}
}
I - Candies (POJ3159)
题目链接
【题意】
给n个人派糖果,给出m组数据,每组数据包含A,B,c 三个数,
意思是A的糖果数比B少的个数不多于c,即B的糖果数 - A的糖果数<= c 。
最后求n 比 1 最多多多少糖果。
【分析】
这是一题典型的差分约束题。不妨将糖果数当作距离,把相差的最大糖果数看成有向边AB的权值,
我们得到 dis[B]-dis[A]<=w(A,B)。看到这里,我们联想到求最短路时的松弛操作,
即if(dis[B]>dis[A]+w(A,B), dis[B]=dis[A]+w(A,B)。
即是满足题中的条件dis[B]-dis[A]<=w(A,B),由于要使dis[B] 最大,
所以这题可以转化为最短路来求。
这题如果用SPFA 算法的话,则需要注意不能用spfa+queue 来求,会TLE ,而是用 spfa + stack。
【Code】
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<stack>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const double EPS = 1e-9;
const int MAX_N = 30000 + 10;
const int MAX_M = 200000 + 10;
struct node{
int from,to,v,next;
} p[MAX_M];
int head[MAX_N],d[MAX_N];
bool v[MAX_N];
int n,m,cnt = 0;
void add(int u,int v,int w)
{
p[cnt].from= u;
p[cnt].to= v;
p[cnt].v= w;
p[cnt].next= head[u];
head[u]= cnt++;
}
void spfa(int s)
{
for(int i=0;i<MAX_N;i++) d[i]=INF;
memset(v,false,sizeof(v));
stack<int> q;
d[s]=0;v[s]=true;
q.push(s);
while (!q.empty())
{
int u=q.top();
q.pop();
v[u]=true;
for (int i=head[u];i!=-1;i=p[i].next)
{
int t=p[i].to;
if (d[t]>d[u]+p[i].v)
{
d[t]=d[u]+p[i].v;
if (!v[t])
{
v[t]=true;
q.push(t);
}
}
}
v[u]=false;
}
}
int main()
{
int x,y,z;
scanf("%d%d",&n,&m);
cnt=0;
memset(head, -1, sizeof(head));
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
//add(y,x,z);
}
spfa(1);
printf("%d\n",d[n]);
}
J - A Walk Through the Forest (HDU1142)
题目链接
【题意】
寻找一共有多少条符合题意的路。能够从点A走到点B的要求是:点A到终点的最短路 > 点B到终点的最短路。
【分析】
从终点出发,求每一个点的最短路,然后那些最短路的值记录起来,作为能否通过的判断条件。最后用记忆化搜索来搜索出一共多少条符合要求的路。
【Code】
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const double EPS = 1e-9;
const int MAX_N = 1000 + 10;
const int MAX_M = 2000000 + 10;
struct node{
int from,to,v,next;
} p[MAX_M];
int head[MAX_N],d[MAX_N];
int f[MAX_N];
int n,m,cnt = 0;
void add(int u,int v,int w)
{
p[cnt].from= u;
p[cnt].to= v;
p[cnt].v= w;
p[cnt].next= head[u];
head[u]= cnt++;
}
void spfa(int s)
{
for(int i=0;i<MAX_N;i++) d[i]=INF;
memset(f,0,sizeof(f));
queue<int> q;
d[s]=0;f[s]=1;
q.push(s);
while (!q.empty())
{
int u=q.front();
q.pop();
f[u]=1;
for (int i=head[u];i!=-1;i=p[i].next)
{
int t=p[i].to;
if (d[t]>d[u]+p[i].v)
{
d[t]=d[u]+p[i].v;
if (!f[t])
{
f[t]=1;
q.push(t);
}
}
}
f[u]=0;
}
}
int dfs(int now)
{
//printf("%d %d\n",now,f[now]);
if (f[now] != -1) return f[now];
if (now == 2) return 1;
f[now] = 0;
for (int i=head[now];i!=-1;i=p[i].next)
if (d[p[i].to]<d[now]){
//printf("%d\n",p[i].to);
f[now] += dfs(p[i].to);
}
return f[now];
}
int main()
{
int x,y,z;
while (~scanf("%d",&n)&&n){
scanf("%d",&m);
cnt=0;
memset(head, -1, sizeof(head));
for (int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
spfa(2);
//printf("%d\n",d[2]);
memset(f,-1,sizeof(f));
cnt = dfs(1);
printf("%d\n",cnt);
}
}
K - Arbitrage (HDU1217)
题目链接
【题意】
在每种钱币间进行各种交换,最后换回自己如果能赚,那么就Yes,否则No
【分析】
以汇率为边,因为是实数,所以有可能为负,用SPFA处理。
然后因为读入的是字符串,所以需要用map处理一下。
【Code】
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const double EPS = 1e-9;
const int MAX_N = 1000 + 10;
const int MAX_M = 2000000 + 10;
struct node{
int from,to,next;
double v;
} p[MAX_M];
int head[MAX_N],t[MAX_N];
bool v[MAX_N];
double d[MAX_N];
int n,m,cnt = 0;
map<string,int> c;
void add(int u,int v,double w)
{
p[cnt].from= u;
p[cnt].to= v;
p[cnt].v= w;
p[cnt].next= head[u];
head[u]= cnt++;
}
bool spfa(int s)
{
memset(d,0,sizeof(d));
memset(v,false,sizeof(v));
memset(t,0,sizeof(t));
queue<int> q;
d[s]=1; v[s]=true;
q.push(s);
while (!q.empty())
{
int u=q.front();
q.pop();
v[u] = true;
//printf("%d\n",head[u]);
for (int i=head[u];i!=-1;i=p[i].next)
{
int t=p[i].to;
//printf("%d --> %d\n",u,t);
if (d[t] < d[u] * p[i].v){
d[t]=d[u] * p[i].v;
if (d[s] > 1.0) return true;
if (!v[t]){
v[t]=true;
q.push(t);
}
}
}
v[u] = false;
}
return false;
}
int main()
{
int Case = 0;
string st,ed;
double df;
while (~scanf("%d",&n) && n){
for (int i=1;i<=n;i++){
cin>>st;
c[st] = i;
}
scanf("%d",&m);
memset(head,-1,sizeof(head));
cnt = 0;
for (int i=0;i<m;i++){
cin>>st>>df>>ed;
add(c[st],c[ed],df);
}
bool flag = false;
for (int i=1;i<=n;i++)
if (spfa(i)){
flag = true;
break;
}
printf("Case %d: %s\n",++Case,flag?"Yes":"No");
}
}