Codeforces Round #270 D C B A
谈论最激烈的莫过于D题了!
看过的两种做法不得不ORZ,特别第二种,简直神一样!!!!!
1th:构造最小生成树。
我们提取所有的边出来按边排序,因为每次我们知道边的权值>0,
之后每次把边加入集合中,不断构造,类似 kruskal算法,构造出边后
再对每个点进行整张图的DFS求距离
复杂度O(N^2lgN):对所有边排序的复杂度。
1 #include<bits/stdc++.h> 2 3 #define N 2222 4 using namespace std; 5 typedef long long ll; 6 #define inf 0x3f3f3f 7 int n; 8 int f[N],vis[N]; 9 int a[N][N]; 10 11 struct node 12 { 13 int u,v,w; 14 node(int uu,int vv,int ww):u(uu),v(vv),w(ww){} 15 }; 16 17 vector<pair<int,int> > G[N]; 18 vector<node> edge; 19 20 int find(int x) 21 {if (f[x]!=x) return f[x]=find(f[x]);} 22 23 int cmp(node a,node b){return a.w<b.w;} 24 25 ll dis[N]; 26 void dfs(int x) 27 { 28 vis[x]=1; 29 // cout<<dis[x]<<" "; 30 for (int i=0;i<G[x].size();i++) 31 { 32 int v=G[x][i].first; 33 int w=G[x][i].second; 34 if (vis[v]) continue; 35 dis[v]=dis[x]+w; 36 dfs(v); 37 } 38 } 39 40 int solve() 41 { 42 for (int i=1;i<n;i++) 43 for (int j=i+1;j<=n;j++) 44 edge.push_back(node(i,j,a[i][j])); 45 46 for (int i=1;i<=n;i++) f[i]=i; 47 48 sort(edge.begin(),edge.end(),cmp); 49 int t=edge.size(); 50 for (int i=0;i<t;i++) 51 { 52 int x=edge[i].u; 53 int y=edge[i].v; 54 int tx=find(x); 55 int ty=find(y); 56 if (tx!=ty) 57 { 58 f[tx]=ty; 59 G[x].push_back(make_pair(y,edge[i].w)); 60 G[y].push_back(make_pair(x,edge[i].w)); 61 } 62 } 63 64 65 for (int i=1;i<=n;i++) 66 { 67 memset(vis,0,sizeof(vis)); 68 memset(dis,0,sizeof(dis)); 69 dfs(i); 70 71 for (int j=1;j<=n;j++) 72 if (dis[j]!=a[i][j]) 73 { 74 return 0; 75 } 76 } 77 return 1; 78 } 79 80 int main() 81 { 82 83 scanf("%d",&n); 84 for (int i=1;i<=n;i++) 85 for (int j=1;j<=n;j++) 86 scanf("%d",&a[i][j]); 87 88 for (int i=1;i<=n;i++) 89 for (int j=1;j<=n;j++) 90 { 91 if (a[i][j]!=a[j][i]||i==j&&a[i][j]!=0) 92 { 93 puts("NO"); 94 return 0; 95 } 96 if (i!=j&&a[i][j]==0) 97 { 98 puts("NO"); 99 return 0; 100 } 101 } 102 103 if (solve()) puts("YES"); 104 else puts("NO"); 105 return 0; 106 }
代码可能更好说明思路,关键一点是:我们每次把最短的边加入集合。我们确定这是一颗最小生成树!所以加边次序是和MST是一样的。
第二种做法不得不佩服!1
这个特种点很难在比赛中发现啊
关键点:我们知道一个点到其他所有点都有一条最小距离的边--假设A到其他点最小距离的点是B,A-B一定是直接连接的。假设距离是X。
然后假设一个点C,C到B点要么比C到A点近X,要么C到A点远X。具体可以画图。
通过这个方法可以判断数据是否合法!
代码超级短
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 using namespace std; 4 #define N 2222 5 6 7 int a[N][N]; 8 int n; 9 int main() 10 { 11 scanf("%d",&n); 12 for (int i=1;i<=n;i++) 13 for (int j=1;j<=n;j++) 14 scanf("%d",&a[i][j]); 15 16 //预处理判断 17 int flag=0; 18 for (int i=1;i<=n;i++) 19 for (int j=1;j<=n;j++) 20 { 21 if (i!=j&&a[i][j]==0) flag=1; 22 if (i==j&&a[i][j]!=0) flag=1; 23 if (i!=j&&a[i][j]!=a[j][i]) flag=1; 24 } 25 26 if (flag) 27 { 28 puts("NO"); 29 return 0; 30 } 31 32 for (int i=1;i<=n;i++) 33 { 34 int inf= 1900000000;//初始的值要比较大些 35 int pos=-1; 36 for (int j=1;j<=n;j++){ 37 if (i==j) continue; 38 if (a[i][j]<inf) 39 { 40 inf =a[i][j]; 41 pos=j; 42 } 43 } 44 for (int j=1;j<=n;j++) 45 { 46 if (i==j||j==pos) continue; 47 if (abs(a[i][j]-a[pos][j])!=inf) 48 { 49 puts("NO"); 50 return 0; 51 } 52 } 53 } 54 55 puts("YES"); 56 return 0; 57 }
后面的基本是水体了:
C:贪心:
从第一的判断每次找合理值,矛盾就错误!
B:
我是用贪心的做法!
想想我们最后电梯还是要回到第一层!对吧!
也要走到最高层,当前有人想去的最高层!
那么我们每次把前K高层的先送上去!就能满足答案最小!
1 #define N 111111 2 using namespace std; 3 4 5 int a[2222]; 6 int cmp(int x,int y) 7 { 8 return x>y; 9 } 10 int main() 11 { 12 int n,k; 13 cin>>n>>k; 14 15 for (int i=1;i<=n;i++) cin>>a[i]; 16 sort(a+1,a+n+1,cmp); 17 int ans=0; 18 for (int i=1;i<=n;i+=k) 19 ans+=(a[i]-1)*2; 20 21 cout<<ans<<endl; 22 return 0;
复杂度O(N^2);时间600MS;这是快
随性Code