最后一次单独拉出来练,虽然觉得还是有太多不足了
Petya and File System
题意:给你几个文件路径,然后问你存放文件和文件夹最多的文件夹是哪个
思路:两个的答案肯定都来自与最上层的某一个文件夹,我们只需要map记录每个文件的路径即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
map<string,int>folder;
//map<string,int>f;
int fa[maxn];
//int fa2[maxn];
int sz2[maxn];
int sz[maxn];
int n;
void inits(){
for(int i = 0;i < maxn-1;i++){
fa[i] = i;
sz[i] = 1;
//fa2[i] = i;
sz2[i] = 1;
}
}
int read(string & file,int loc){
loc++;
int flag = 0;
while(file[loc]!='\\'){
if(file[loc] == '.'){
flag = 1;
break;
}
loc++;
}
if(flag == 1){
return file.size();
}
return loc;
}
int get(int x){
if(fa[x]==x)return x;
return fa[x] = get(fa[x]);
}
void merge(int u,int v){
int p = get(u);
int q = get(v);
if(p!=q){
fa[q] = p;
sz[p] += sz[q];
}
}
//int get2(int x){
// if(fa2[x]==x)return x;
// return fa2[x] = get(fa2[x]);
//}
//void merge2(int u,int v){
// int p = get2(u);
// int q = get2(v);
// if(p!=q){
// fa2[q] = p;
// sz2[p] += sz2[q];
// }
//}
int main(){
string s;
int cnt = 0;
int ct = 0;
inits();
while(cin>>s){
int p = 2;
int n = s.size();
int st = -1;
while(p<n){
int las = read(s,p);
string tmp = s.substr(0,las);
// cout<<tmp<<endl;
if(las<n){
int k = -1;
if(folder.count(tmp)==0){
folder[tmp] = ++cnt;
// cout<<"KK"<<endl;
}
k = folder[tmp];
if(st == -1){
st = k;
}
// cout<<tmp<<"##"<<cnt<<endl;
merge(st,k);
}
else {
//int k = -1;
//if(f.count(tmp)==0){
//f[tmp] = ++ct;
//}
//k = f[tmp];
//merge2(st,k);
sz2[st]++;
}
p = las;
}
}
int ans = 0;
for(int i = 1;i <= cnt;i++){
ans = max(ans,sz[get(i)]);
}
cout<<ans-1<<" ";
ans = 0;
for(int i = 1;i <= cnt;i++){
//ans = max(ans,sz2[get2(i)]);
ans = max(ans,sz2[i]);
}
cout<<ans-1<<endl;
}
Trucking
题意:给你一个无向图,然后每个边上有限高和限长度,然后有一个货车从起点出发到达终点,问,在保证限高最大的情况下,最短路是多少
思路:
使用了spfa迭代维护了限高的最大值,然后在按照我们求得的限高跑一边dijstra就好了
网上的思路是二分枚举限高,然后spfa或者dijstra跑最短路
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e6+5;
const int N = 1005;
struct edge{
int v,limt,w,next;
}e[maxn<<1];
int head[N],cnt;
void add(int u,int v,int limt ,int w){
e[cnt].v = v;
e[cnt].limt = limt;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt++;
}
queue<int>q;
int mxh[N];
int dis[N],vis[N],ct[N];
int n,m;
void inits(){
for(int i = 0 ;i <= n;i++){
head[i] = -1;
dis[i] = INF;
mxh[i] = vis[i] = ct[i] = 0;
}
cnt = 0;
while(!q.empty())q.pop();
}
int SPFA(int s,int ed,int h){
q.push(s);
vis[s] = 1;ct[s] = 1;dis[s] = 0;
mxh[s] = h;
while(!q.empty()){
int now = q.front();
q.pop();
vis[now] = 0;
//cout<<now<<"##"<<mxh[now]<<endl;
for(int i = head[now];~i;i=e[i].next){
int v = e[i].v;
int w = e[i].w;
int limt = e[i].limt;
if(mxh[v] < min(mxh[now],limt)){
//dis[v] = dis[now] + w;
mxh[v] = min(mxh[now],limt);
if(!vis[v]){
ct[v]++;
vis[v] = 1;
q.push(v);
if(ct[v]>n)return 1;
}
}
}
}
return 0;
}
struct Node{
int id , d;
bool operator < (const Node &x)const{
return d > x.d;
}
};
priority_queue<Node>Q;
void dijkstra(int s,int ed,int h){
while(!Q.empty())Q.pop();
memset(vis,0,sizeof vis);
Q.push(Node{s,0});
dis[s] = 0;
while(!Q.empty()){
int u = Q.top().id;Q.pop();
if(vis[u])continue;
vis[u] = 1;
for(int i = head[u];~i;i=e[i].next){
int v = e[i].v;
int l = e[i].limt;
if(l < h)continue;
if(dis[v]>dis[u] + e[i].w){
dis[v] = dis[u] + e[i].w;
Q.push(Node{v,dis[v]});
}
}
}
}
int main(){
int tt = 0;
while(1){
scanf("%d%d",&n,&m);
if(n==0&&m==0)break;
inits();
for(int i = 0;i < m;i++){
int u,v,limt,w;
scanf("%d%d%d%d",&u,&v,&limt,&w);
if(limt==-1)limt = INF;
add(u,v,limt,w);add(v,u,limt,w);
}
int st,ed,h;
scanf("%d%d%d",&st,&ed,&h);
SPFA(st,ed,h);
dijkstra(st,ed,mxh[ed]);
if(tt!=0)printf("\n");
printf("Case %d:\n",++tt);
if(dis[ed]>=INF)printf("cannot reach destination\n");
else {
printf("maximum height = %d\n",mxh[ed]);
printf("length of shortest route = %d\n",dis[ed]);
}
}
}
maximum shortest distance
题意:
在一张平面上给定一些点,然后让你选取k个点,使得选取点集中两点距离的最小值最大
思路:
题解说看到这种最小值最大的情况,就是二分,我确实也想到了二分实数,但是剩下的部分我想错了,应该是剩下求一个最大团,就是保证我们答案的同时建一个新图,然后求一个最大团,抄了网上板子过了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 55;
const double esp = 1e-4;
double mp[maxn][maxn];
struct Node{
int x,y;
}node[maxn];
int k;
int mx;//最大团数(要初始化为0)
int vis[maxn], tuan[maxn];
int can[maxn][maxn];//can[i]表示在已经确定了经选定的i个点必须在最大团内的前提下还有可能被加进最大团的结点集合
int num[maxn];//num[i]表示由结点i到结点n构成的最大团的结点数
bool g[maxn][maxn];//邻接矩阵(从1开始)
int n, m;
bool dfs(int tot, int cnt) {
if(tot == 0) {
if(cnt > mx) {
mx = cnt;
for(int i = 0; i < mx; i++) {
tuan[i] = vis[i];
}
return true;
}
return false;
}
for(int i = 0; i < tot; i++) {
if(cnt + (tot - i) <= mx) return false;
if(cnt + num[can[cnt][i]] <= mx) return false;
int k = 0;
vis[cnt] = can[cnt][i];
for(int j = i + 1; j < tot; j++) {
if(g[can[cnt][i]][can[cnt][j]]) {
can[cnt + 1][k++] = can[cnt][j];
}
}
if(dfs(k, cnt + 1)) return false;
}
return false;
}
void maxclique() {
mx = 1;
for(int i = n; i >= 1; i--) {
int k = 0;
vis[0] = i;
for(int j = i + 1; j <= n; j++) {
if(g[i][j]) {
can[1][k++] = j;
}
}
dfs(k, 1);
num[i] = mx;
}
}
double MKDis(Node a,Node b){
return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
//int n,k;
bool ok(double x){
memset(g,0,sizeof g);
for(int i = 1;i<=n;i++){
for(int j = 1; j< i;j++){
if(MKDis(node[i],node[j]) >= x)g[i][j] = g[j][i] = 1;
}
}
maxclique();
if(mx >= k)return true;
return false ;
}
int main(){
while(cin>>n>>k){
for(int i = 1;i <= n; i++){
scanf("%d%d",&node[i].x,&node[i].y);
for(int j = 1;j < i; j++){
mp[i][j] = mp[j][i] = MKDis(node[i],node[j]);
}
}
double l = 0,r = 1e9;
double ans = 0;
while(r-l > esp){
double mid = (l+r)/2;
if(ok(mid)){
// ans = max(ans,mid);
l = mid;
}
else r = mid;
}
// cout<<ans<<endl;
printf("%.2f\n",l);
}
}
Bicolorings
题意:给你一个2*n的方格,然后染黑白两色,问最后染出来的色块有k个的情况到底有多少种染色方法
思路:线性dp,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示当前染色到前i个方块,染出色块j个的答案数
思路非常好想,就是下一个位置可以由上一个位置装换过来,并且可以优化掉一维,然后还有一种状压dp的思想记录最后一列的情况,但是调试了很久。
最终确定第一维转移是位置转移,由i-1转移到i,第二位转移为情况属染出色块个数的转移,由于最后一行和倒数第二行分布,我们可以写出dp转移公式,然后倒序转移!因为我们转移是按照当前行的转移到下一行,我们优化了一维就要倒序转移,另外为了放置内部影响,我们要那变量倒换一下。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1005;
const int mod = 998244353;
ll dp[maxn<<1][10];
int main(){
int n,k;
scanf("%d%d",&n,&k);
dp[1][0] = 1;
dp[1][1] = dp[1][2] = 0;
dp[1][3] = 1;
dp[2][1] = dp[2][2] = 1;
// dp[0][0] = dp[0][1] = dp[0][2] = dp[0][3] = 1;
for(int j = 2;j <= n;j++){
for(int i = k;i >= 2;i--){
ll a = (dp[i][0] + dp[i][1] + dp[i][2] + dp[i-1][3])%mod;
ll b = (dp[i-1][0] + dp[i][1] + dp[i-2][2] + dp[i-1][3])%mod;
ll c = (dp[i-1][0] + dp[i-2][1] + dp[i][2] + dp[i-1][3])%mod;
ll d = (dp[i-1][0] + dp[i][1] + dp[i][2] + dp[i][3])%mod;
dp[i][0] = a;dp[i][1] = b;dp[i][2] = c;dp[i][3] = d;
}
}
cout<<(dp[k][0] + dp[k][1] + dp[k][2] + dp[k][3])%mod;
}
Armchairs
题意:给你 n n n个空位置,然后给你n/2以下个被占了的座位,然后需要我们将被占了的座位上的人都移动到其他的空位置上,代价是 ∣ i − j ∣ |i-j| ∣i−j∣问你最小花费
思路:和我上一个写的题解的一道题思路相仿,就是n个物品,放置在n个位置,然后放置在每个位置都有对应的花费,求我们的最小花费,就是一个背包,体积为n每个物品的价值为
∣
i
−
j
∣
|i-j|
∣i−j∣
然后就是注意两个维度都要从小往大排一个序,贪心想法,保证无后效性
网上的思路:比较裸的最小费用最大流
我们将源点和每一个被占了的座位连接,流量为1,费用为0,每一个没被占的座位和汇点连接,流量为1,费用为0,每两个座位之间连一条边,流量为n nn,费用为1,跑E K即可
AC代码
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 5005;
int a[maxn],b[maxn];
int dp[maxn];
int main(){
int n;
scanf("%d",&n);
int cta = 0,ctb = 0;
for(int i = 1;i <= n;i++){
int x;scanf("%d",&x);
if(x==0){
a[++cta] = i;
}
else b[++ctb] = i;
}
memset(dp,INF,sizeof dp);
dp[0] = 0;
for(int i = 1;i <= cta;i++){
for(int j = ctb;j >= 1;j--){
dp[j] = min(dp[j],dp[j-1] + abs(a[i]-b[j]));
}
}
cout<<dp[ctb]<<endl;
}
Equivalent Sets
- HDU - 3836
题意:给你一堆集合的关系,然后问再给你多少关系可以把他们变成等价集合。
思路:
首先,我们有结论就是如果一个强连通分量就是等价的,所以我们先缩点,然后看剩下的点的入度为0的个数和出度为0个数的最大值。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e4+5;
const int maxn = 5e4 + 5;
int n,m;
struct edge{
int v,next;
}e[maxn<<1];
int head[N],cnt;
void add(int u,int v){
e[cnt].v = v;
e[cnt].next = head[u];
head[u] = cnt++;
}
int dfn[N],low[N],c[N];
int ins[N];
int st[N],top = 0;
int num = 0;
int lis_nu = 0;
int ind[N],outd[N];
void inits(){
cnt = 0;
memset(head,-1,sizeof head);
for(int i = 0;i <= n;i++){
dfn[i] = low[i] = ins[i] = c[i] = 0;
ind[i] = outd[i] = 0;
}
top = 0;
lis_nu = 0;
num = 0;
}
void Tarjan(int x){
low[x] = dfn[x] = ++num;
st[++top] = x;
ins[x] = 1;
for(int i = head[x];~i;i = e[i].next ){
int v = e[i].v;
if(!dfn[v]){
Tarjan(v);
low[x] = min(low[x],low[v]);
}
else if(ins[v])low[x] = min(low[x],dfn[v]);
}
if(low[x]==dfn[x]){
int t;
lis_nu++;
do{
t = st[top--];
c[t] = lis_nu;
ins[t] = 0;
}while(t!=x);
}
}
struct Qu{
int u,v;
}qu[maxn];
int main(){
while(~scanf("%d%d",&n,&m)){
inits();
for(int i = 0;i < m; i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
qu[i].u = u;qu[i].v = v;
}
for(int i = 1;i<=n;i++){
if(!dfn[i])Tarjan(i);
}
for(int i = 0;i < m;i++){
int u = c[qu[i].u];
int v = c[qu[i].v];
if(u==v)continue;
ind[v]++;
outd[u]++;
}
int mxin = 0,mxout = 0;
for(int i = 1;i <= lis_nu;i++){
if(ind[i]==0){
mxin++;
}
if(outd[i]==0){
mxout++;
}
}
printf("%d\n",lis_nu==1?0:max(mxin,mxout));
}
}
Zjnu Stadium
题意:
一个300的体育场,然后是一圈样子的,然后我们可以指定两个人的位置关系,不如a在b右边30个队列出,然后问你有几个关系是错的
思路:
边带权的板子题:
然后就是注意正确的维护集合之间的关系。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5+5;
const int mod = 300;
int fa[maxn];
int sz[maxn];
int d[maxn];
int get(int x){
if(fa[x]==x)return x;
int root = get(fa[x]);
d[x] = (d[x] + d[fa[x]])%mod;
return fa[x] = root;
}
void merge(int u,int v,int w){
int p = get(u);
int q = get(v);
if(p!=q){
fa[p] = q;
d[p] = (w - d[u] + d[v] + mod)%mod;
}
}
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
for(int i = 0;i <= n;i++){
fa[i] = i;
d[i] = 0;
}
int ans = 0;
for(int i = 0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(get(u)==get(v)){
if((d[u]-d[v]+mod)%mod!=w)ans++;
}
else merge(u,v,w);
}
printf("%d\n",ans);
}
}
Mike and gcd problem
题意:
给你一个序列a,然后可以有如下操作,
delete numbers
a
i
,
a
i
+
1
a_i, a_i + 1
ai, ai + 1 and put numbers
a
i
−
a
i
+
1
,
a
i
+
a
i
+
1
a_i - a_i + 1, a_i + a_i + 1
ai − ai + 1, ai + ai + 1 in their place instead, 然后问最少多少次这样的操作可以使整个序列的gcd大于1,然后gcd(0,2)=2.
思路:
先直接正向维护一遍gcd,如果已经是大于1的我们就不需要任何操作了,如果不是我们就需要改变一些数,构造他们的gcd为2,想一想为什么,因为gcd=1肯定有互质的数,我们如何将这个互质的数变成有公共因子的数,就是把所有数的公共因子变成2,我们就正向扫描把奇数和奇数变成两个偶数,然后把奇数和偶数变成两个偶数,注意这样需要两次操作。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
int gcd(int a,int b){
if(b==0)return a;
return gcd(b,a%b);
}
int main(){
int n;
scanf("%d",&n);
for(int i = 1;i<=n;i++){
scanf("%d",&a[i]);
}
int g = a[1];
for(int i = 2;i<=n;i++){
int k = a[i];
if(g<a[i])swap(g,k);
g = gcd(g,k);
}
if(g>1){
printf("YES\n0");return 0;
}
int ans = 0;
for(int i = 1;i <= n;i++){
if(a[i]%2==0)continue;
if((a[i]&1)&&(a[i+1]&1)){
ans += 1;
a[i] = 2;a[i+1] = 2;
}
else ans += 2;
}
printf("YES\n");
printf("%d",ans);
}