个字符串序列,从中随机取两个 按先后顺序 组成完整字符串,判断这
为什么把这个题也列出来就是单纯的为了强调 按先后顺序 这个事儿。需要注意,按照
还有就是
#include <bits/stdc++.h>
using namespace std;
string s[1001];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
for (int i = 1; i <= n ; i++)
{
for (int j = 1; j <= n; j++)
{
if(i==j)continue;
string f = s[i] + s[j];
reverse(f.begin(), f.end());
if (f == s[i] + s[j])
{
cout << "Yes" << endl;
system("pause");
return 0;
}
}
}
cout << "No" << endl;
system("pause");
return 0;
}
题目大意
高桥有两张不大于
格纸
但是要注意,目标格纸的大小最大也不超过
所以设无限大方格纸就是
枚举模拟
但是还有一个问题,期望矩形
一种方法是模拟
#include <bits/stdc++.h>
using namespace std;
int a[40][40], b[40][40], c[40][40], anss[40][40];
int ax, ay, bx, by, cx, cy;
int main()
{
string s;
cin >> ax >> ay;
for (int i = 1; i <= ax; i++)
{
cin >> s;
for (int j = 0; j < s.size(); j++)
a[i][j + 1] = (s[j] == '#');
}
cin >> bx >> by;
for (int i = 1; i <= bx; i++)
{
cin >> s;
for (int j = 0; j < s.size(); j++)
b[i][j + 1] = (s[j] == '#');
}
cin >> cx >> cy;
for (int i = 1; i <= cx; i++)
{
cin >> s;
for (int j = 0; j < s.size(); j++)
c[10 + i][11 + j] = (s[j] == '#');//放在大矩阵的中间
}
for (int i = 1; i <= 20; i++)
for (int j = 1; j <= 20; j++)//A矩阵启动位置
for (int m = 1; m <= 20; m++)
for (int k = 1; k <= 20; k++)//B矩阵启动位置
{
bool tag = 1;
memset(anss, 0, sizeof(anss));
for (int i_ = 1; i_ <= ax; i_++)
for (int j_ = 1; j_ <= ay; j_++)
{
if (a[i_][j_])
anss[i_ - 1 + i][j_ - 1 + j] = 1;
}
for (int i_ = 1; i_ <= bx; i_++)
for (int j_ = 1; j_ <= by; j_++)
{
if (b[i_][j_])
anss[i_ - 1 + m][j_ - 1 + k] = 1;
}
for (int x = 1; x <= 30; x++)//判断合法区域就只为30x30
for (int y = 1; y <= 30; y++)
{
if (anss[x][y] != c[x][y])
tag = 0;
}
if (tag)
{
cout << "Yes" << endl;
system("pause");
return 0;
}
}
cout << "No" << endl;
system("pause");
return 0;
}
题目大意
环形的涂色问题,有一个有
设数列
易有
而将
所以
等比数列待定系数法,设
解有
#include <bits/stdc++.h>
using namespace std;
typedef long long ull;
const ull MOD = 998244353;
ull as[10000001];
int main()
{
int n, m;
cin >> n >> m;
ull ans = 1;
as[1] = m - 1;
for (int i = 2; i <= n; i++)
as[i] = ((as[i - 1]) * (m - 1)) % MOD;
if (n & 1)
cout << (as[n] + MOD - (m - 1)) % MOD << endl;
else
cout << (as[n] + (m - 1) ) % MOD << endl;
system("pause");
return 0;
}
题目大意
一张有
一开始的时候想法很简单,就是以第
意识到这个问题之后,就换了第二种方式,对每一次节点都重新跑一次最短路径算法;但是这会产生一个新的问题:首先是存在第
错误代码1
#include <bits/stdc++.h>
using namespace std;
struct nexts // 邻接表
{
int pos;
int w;
};
int n, m, d, k; // 房间数,走廊数,总传染天数,初始感染者人数
struct node // 最短路径队列维护
{
long long dist;
int pos;
int infectday;
bool operator<(const node &x) const
{
return x.dist < dist;
}
};
vector<nexts> connects[300001];
priority_queue<node> q;
long long dist[300001]; // 最短路径
bool visited[300001]; // 最短路径
bool infectcheck[300001]; // 是否感染
int A[300001]; // 初始感染者位置
long long D[300001]; // 第i天传染力距离
int infectdays[300001];
void initdist()
{
for (int i = 1; i <= 300001; i++)
{
dist[i] = 1e10;
}
}
void initvisited()
{
memset(visited, false, sizeof(visited));
}
void Dijkstra_queue(int start)
{
dist[start] = 0;
q.push((node){0, start});
while (!q.empty())
{
int location = q.top().pos;
q.pop();
if (visited[location])
continue;
visited[location] = true;
for (int i = 0; i < connects[location].size(); i++)
{
int v = connects[location][i].pos;
int w = connects[location][i].w;
if (dist[v] > dist[location] + w)
{
dist[v] = dist[location] + w;
if (!visited[v])
q.push((node){dist[v], v});
}
}
}
return;
}
void Final_queue(int k)
{
initdist();
cin >> d;
for (int i = 1; i <= d; i++)
cin >> D[i];
queue<node> q;
for (int i = 1; i <= k; i++)
q.push((node){0, A[i], 0});
int start = 0;
int infectday = 0;
while (!q.empty()) // 存在一种可能,当某一天可感染成员队列为空(第i天无法感染任何人)
{
start = q.front().pos;
infectday = q.front().infectday;
q.pop();
infectcheck[start] = true;
infectdays[start] = infectday;
if (infectday >= d)
continue;
initvisited();
Dijkstra_queue(start);
for (int i = 1; i <= n; i++)
{
if (!infectcheck[i] && D[infectday + 1] >= dist[i])
{
q.push({dist[i], i, infectday + 1});
infectcheck[i] = true;
infectdays[i] = infectday + 1;
}
}
if (q.empty() && infectday < d - 1)
// 感染队列为空的原因有两种,一是第i天无法感染任何人,需要下一步if;二是天数已满,统计结束,直接跳出.
// 而当第d-1天的感染者无法感染任何人时,直接跳出;否则要进行手动跨天。
{
for (int i = 1; i <= n; i++) // 如果不是第d-1天
{
if (!infectcheck[i] && D[infectday + 2] >= dist[i])
{
q.push({dist[i], i, infectday + 2});
infectcheck[i] = true;
infectdays[i] = infectday + 2;
}
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1, u, v, w; i <= m; i++)
{
cin >> u >> v >> w;
connects[u].push_back((nexts){v, w});
connects[v].push_back((nexts){u, w}); // 邻接表双向图
}
cin >> k;
memset(infectdays, -1, sizeof(infectdays));
for (int i = 1; i <= k; i++)
cin >> A[i], infectdays[A[i]]++;
Final_queue(k);
for (int i = 1; i <= n; i++)
cout << infectdays[i] << endl;
system("pause");
return 0;
}
后来意识到按照点的传染能力计算天数可行性不高(关于中间无感染天数的计算难度过大且浪费大量时间复杂度),最后决定更换思路,按照每一天计算传染情况。这其实也就是对
对于第
这样按照一个节点一个节点的跑最短路径相当于将
同时还涉及到的问题是,由于是多源点最短路径,所以存到队列的时候的时候会有一步局限性,需要这句话:
if(dis>dist[v])continue;
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> nexts;
vector<nexts> connects[300001];
priority_queue<nexts, vector<nexts>, greater<nexts>> qfirst, qothers;
// qfirst的含义是在第i-1天的感染者辐射下的密接圈内的人,qothers含义是指在第i-1天感染者辐射下次密接及次次密接
// 等非密接但是能被感染距离污染的人
int dist[300001], infectdays[300001];
int n, m, k, d;
void cin_graph(int &n, int &m) // 全部初始化
{
cin >> n >> m;
for (int i = 1, u, v, w; i <= m; i++)
{
cin >> u >> v >> w;
connects[u].push_back(nexts(v /*pos*/, w /*w*/));
connects[v].push_back(nexts(u, w));
}
for (int i = 1; i <= n; i++)
{
dist[i] = 2147483647;
infectdays[i] = -1;
}
return;
}
int main()
{
cin_graph(n, m);
cin >> k;
for (int i = 1; i <= k; i++)
{
int location; // 初始感染者坐标;
cin >> location;
dist[location] = 0;
infectdays[location] = 0;
for (int j = 0; j < connects[location].size(); j++)
{
int u = connects[location][j].first;
int w = connects[location][j].second;
if (dist[u] > dist[location] + w)
{
dist[u] = dist[location] + w; // 第i-1天的感染者在第i天可能的密接圈路径
qfirst.push(nexts(dist[u], u)); // 单对起点,由于第i-1天的感染者并不唯一,所以存入临时密接圈
// 的dist数值只是说明当前location下的距离而非最短距离(可能两个点能够同时到一个点但距离不一)
// ,但是一直在全局的dist数组却是能保证第i-1天感染者组成的集合针对其他密接圈内人的最短距离
}
}
}
cin >> d;
for (int i = 1; i <= d; i++) // 彻底转变思路,以病毒爆发第i天来进行规划。
// 病毒爆发的第i天,由第i-1天及以前的所有人组成的集合向外辐射最大距离为Di的病毒污染人群。而污染分成了两大类情况。
/*1.感染者距离不够感染任何人(感染距离小于密接圈最短距离)。
2.感染者距离恰好能够感染密接圈内的某人(密接圈外距离够不到)。
3.感染者距离能够辐射到密接圈之外的人。
如果单纯以密接圈内所有人为基准进行最短路径规划会有非常恐怖的时间复杂度(大约是n^2,这还是不计算队列优化的
Dijkstra的nlogn的前提下,面对3e5的数据绝对超时,所以我们优化计算的时候,仅对可能辐射到次密接的部分进行
Dijkstra_queue计算,减少大面积的无必要的计算。
*/
{
int ability;
cin >> ability; // 第i天的最大感染辐射范围。
vector<nexts> infectedperson;
while (!qfirst.empty())
{
nexts node = qfirst.top();
qfirst.pop();
int dis = node.first, location = node.second;
if (dis > dist[location])
continue; // 这就是涉及到上文注释过的点集合到目标位置的最短距离筛选,只有两者
// 相等的时候才是最短距离,符合题目要求。
if (ability < dis)
{
qfirst.push(node);
break;
} // 1.密接圈内该人不能被感染,此时由于优先队列特性,node后面所有的节点都不可能被感染,所以直接打破
// 密接圈排查,密接圈内剩余人将在第二天继续排查。
/*
这里同时涉及到了一个greater<pair<int,int>>的特性,该函数的含义如下:
template<typename T> struct greater
{
constexpr bool operator()(T const& a, T const& b) const
{
return a.first>b.first || ( (a.first<b.first) && (a.second>b.second));
}
};
*/
qothers.push(nexts(dis, location));
}
while (!qothers.empty())
{
nexts node = qothers.top();
qothers.pop();
int dis = node.first, location = node.second;
if (dis > dist[location])
continue; // 同理,多源起点取最短距离必须有部分。
infectedperson.push_back(node);
for (int j = 0; j < connects[location].size(); j++) // 既然目标人已经进入感染的辐射范围,
// 那么探查其是否可能存在辐射到次密接的可能性,进行最短路径。
{
int v = connects[location][j].first;
int w = connects[location][j].second;
if (dist[v] > dist[location] + w)
{
dist[v] = dist[location] + w;
if (dist[v] <= ability) // 从该路径仍存在继续辐射的可能性
qothers.push(nexts(dist[v], v));
}
}
} // qothers变为空的时候,infectperson即保存了全部在第i天被感染的人。
for (int j = 0; j < infectedperson.size(); j++)
{
int location = infectedperson[j].second;
infectdays[location] = i;
dist[location] = 0; // 作为第i批被感染的人去初始化感染第i+1天的人。
for (int p = 0; p < connects[location].size(); p++)
{
int u = connects[location][p].first;
int w = connects[location][p].second;
if (dist[u] > dist[location] + w)
{
dist[u] = dist[location] + w;
qfirst.push(nexts(dist[u], u));
}
}
}
}
for (int i = 1; i <= n; i++)
cout << infectdays[i] << endl;
system("pause");
return 0;
}
题目大意
给定一串数列
求使得数列各项之间绝对值差小于等于1的最少操作次数。
设数列的总和为
最终状态的数列
设数列初始时每一项的移动为
所以,最终序列的各项排列顺序决定了数列的操作次数,故进行
设
对于
前缀和的计算是将所有项全部调整成为了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=0x7fffffffffffff;//无穷大初始化
ll a[5001],b[5001],sum,ave,rem;
ll dp[5001][5001];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
ave=sum/n;
if(ave<=0)ave--;
rem=sum-ave*n;
for(int i=1;i<=n;i++)
{
b[i]=b[i-1]+a[i]-ave;//假设全变为ave需要调整的次数
}
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
dp[i][j]=INF;
}
}
dp[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=i&&j<=rem;j++)
{
if(j==0)dp[i][j]=dp[i-1][j];
else
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j]);
dp[i][j]+=abs(b[i]-j);
//解释一下为什么要b[i]-j
/*
本题的最少次数就是一个一个的复位匹配。那么我们在复原的时候,复原每一个数时所产生的副作用
都会被下一个数所负担,这样就会有负担次数的利滚利。
而对于前i个数的复原时,本来应该有j个数是ave+1但却复原成为了ave。那么,第一个应该被复原为ave+1
的数多产生了一次复原的负担并且使下一个数的复原过程时多了一步,下下个数的复原过程又多了一步……
同理,这j个特殊的数字将会对i后面的每一个数多产生j个数字的负担,而我们现在需要ave+1,
所以也就需要对后面的每一个数字抵消j次的额外操作负担。
*/
}
}
cout<<dp[n][rem]<<endl;
system("pause");
return 0;
}
考点:快速傅里叶变换(能力范围之外,暂留)
请我喝杯咖啡吧~