7.26总结前日
这两天rating还可以,继续保持
队内赛链接:
D:
题意:给你一张折纸,这张这只分成了很多的方块,每次折叠会将所有方块上的值相累加,然后问你能否通过有限次折叠变成最终目标的状态
解法:计算出所有可能的值,存到set里面查询。。。。
说实话感觉是dp那一块的,但是并不怎么会
#include<cstdio>
#include<iostream>
#include<set>
using namespace std;
typedef long long ll;
ll v[1000], w[1000], sum1, sum2, n, m;
set<ll>s;
//暂时觉得应该是dp的思想,先留着等做完下一章的题再来看
void calc(int now, int left, int right) {
if (left > right) { s.insert(now); return; }
calc(now + v[left], left + 1, right);
calc(now, left + 1, right);
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &v[i]);
scanf("%d", &m);
if (m > n) printf("N");
else {
for (int i = 0; i < m; i++) scanf("%d", &w[i]);
calc(0, 0, n - 1);
for (int i = 0; i < m; i++) {
if (s.find(w[i]) == s.end()) {
printf("N"); return 0;
}
}
printf("S");
}
return 0;
}
F:
题意:有一个圆,然后给出你所有点离原点的距离,然后问你这些点可以构成多少个等边三角形。
解法:首先我们知道等边三角形将圆弧平均分成三条,由于给出了所有点之间的距离,所以我们可以计算他们的前缀和,之后我们枚举起点,通过比较他们的前缀和之间是否符合三段圆弧之间不同的线段长度即可
#include<cstdio>
#include<iostream>
#include<set>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
int a[maxn];ll sum[maxn];
multiset<ll>s;
int main() {
int n; scanf("%d", &n);
int tot = 0; sum[0] = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
tot += a[i];
}
for (int i = 1; i <= n; i++) {
s.insert(sum[i]);
}
if (tot % 3 != 0) { printf("0"); return 0; }
int len = tot / 3, ans = 0;
for (int i = 1; i <= n; i++) {
int pos1 = len + sum[i];
int pos2 = len + len + sum[i];
if (s.find(pos1) != s.end() && s.find(pos2) != s.end()) {
ans++;
}
}
printf("%d", ans);
return 0;
}
G题:
题意:有一个矩阵,然而这个矩阵的行列上的元素是乱序的,要求每次挪动一行或者一列,要求将最后的矩阵恢复成最初的样子
解法:这个其实实现的方式很多,但这份代码的思路非常好,多看多借鉴
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 1e5 + 5;
int ori[400][400], row[400][400], col[400][400];
int n, m, R[maxn], L[maxn], a[maxn], b[maxn], vis[400];
int check(int rownum,int colnum) {
int flag = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < rownum; j++) {
if (R[row[i][j]] != R[row[i][0]]) return 0;
}
a[i] = R[row[i][0]];
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < colnum; j++) {
if (L[col[i][j]] != L[col[i][0]]) return 0;
}
b[i] = L[col[i][0]];//B储存的是每一行的开头元素所在的列
}
return 1;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &ori[i][j]);
}
}
int rownum = 0,colnum = 0;
for (int i = 0; i < n; i++) {
rownum = 0;
for (int j = 0; j < m; j++) {
row[i][rownum++] = i * m + j + 1;
col[j][colnum] = i * m + j + 1;
R[ori[i][j]] = i;
L[ori[i][j]] = j;
}
colnum += 1;
}
if (!check(m, n)) {
printf("*");
return 0;
}
int ans1 = 0, ans2 = 0;
for (int i = 0; i < n; i++) {
if (a[i] != i && vis[i] == 0) {
int cnt = 0;
for (int j = i; vis[j] == 0; ) {
vis[j] = 1, cnt++;
j = a[j];
}
ans1 += cnt - 1;
}
}
memset(vis, 0, sizeof(vis));
for (int i = 0; i < m; i++) {
if (b[i] != i && vis[i] == 0) {
int cnt = 0;
for (int j = i; vis[j] == 0; ) {
vis[j] = 1, cnt++;
j = b[j];
}
ans2 += cnt - 1;
}
}
printf("%d", ans1 + ans2);
return 0;
}
Uva1614
题意:输入一个长度为n的序列a,满足1<=a[i]<=i,要求确定每个数的正负号,使得所有数的总和为0
解法:代码顶部给出
//贪心部分的理论依据:前i个数可以凑出1~sum[i]的所有整数。
//因为要所有的和为0,所以只要sum是偶数,我们凑出sum的一半就行了
//1 3 sum[2]==4,那么前两个数能凑出来1~4的所有整数吗?
//所以这个应该是不完全正确的
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int pos[100000 + 10];
struct node {
int val, id;
bool operator < (node &rhs) { return val < rhs.val; }
};
int main() {
int n;
while (scanf("%d", &n) != EOF) {
ll sum = 0;
memset(pos, 0, sizeof(pos));
node a[100000 + 10];
for (int i = 0; i < n; i++) {
scanf("%d", &a[i].val);
a[i].id = i;
sum += a[i].val;
}
if (sum & 1) { printf("No\n"); continue; }
sort(a, a + n); sum >>= 1;
for (int i = n - 1; i >= 0; i--) {
if (a[i].val <= sum) { sum -= a[i].val; pos[a[i].id] = 1; }
else pos[a[i].id] = -1;
}
printf("Yes\n%d", pos[0]);
for (int i = 1; i < n; i++) {
printf(" %d", pos[i]);
}printf("\n");
}
return 0;
}
Uva1615
题意: 给定平面上n个点和一个值D,要求在x轴上选出尽可能少的点使得对于给定的每个点,都有一个选出的点离他的欧几里得距离不超过D
解法:这道题竟然不是求交集,而是贪心解的
//贪心,不用求所有区间的最小交集个数
//排序,贪心的原则是每次选区间的右端点
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
typedef double db;
struct node {
db l, r;
bool operator < (node const &rhs) const{
return r < rhs.r || (r == rhs.r && l > rhs.l);//小区间一定在左边
}
};
node a[10000 + 5];
int main() {
int L, d, n;
while (scanf("%d", &L) != EOF) {
scanf("%d%d", &d, &n);
int cnt = 0;
for (int i = 0; i < n; i +=1 ) {
db x, y;
scanf("%lf%lf", &x, &y);
if (d < y)continue;
else if (d == y) a[cnt++] = { x,x };
else {
a[cnt].l = x - sqrt(d*d - y*y);
a[cnt++].r = x + sqrt(d*d - y*y);
}
}
sort(a, a + cnt); int ans = 1;
int now = a[0].r;
for (int i = 0; i < cnt; i++) {
if (a[i].l > now) {
ans++; now = a[i].r;
}
}
printf("%d\n", ans);
}
return 0;
}
Uva1618‘
题意:给出k个互不相同的整数组成的序列Ni,判断是否存在4个整数Np,Nq,Nr,Ns
其中(1<= p < q < r < s <=k),使得Nq>Ns>Np>Nr或者Nq < Ns < Np < Nr
解法:见代码上部
/*
首先预处理maxm[i][j],表示从i开始到j的最大值;
然后再处理vec[i],表示从i + 1开始所有大于a[i]的数,并排序。
先枚举p和r,在根据maxm[i][j]直接选取最大的Nq,
最后通过二分在vec[j]中查找符合条件的Ns,如果找到直接返回true,否则返回false。
对输入的数列正序和逆序都处理一遍即可。
*/
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 5000 + 10;
int a[maxn], b[maxn], maxm[maxn][maxn];
vector<int>vec[maxn];
int n;
bool judge(int x[]) {
for (int i = 0; i < n; ++i){
int tmp = x[i];
maxm[i][i] = x[i];
for (int j = i + 1; j < n; ++j)
if (x[j] > tmp){maxm[i][j] = x[j];tmp = x[j];}
else maxm[i][j] = maxm[i][j - 1];
}
for (int i = 0; i < n; ++i){
vec[i].clear();
for (int j = i + 1; j < n; ++j)
if (x[j] > x[i]) vec[i].push_back(x[j]);
sort(vec[i].begin(), vec[i].end());
}
for (int i = 0; i < n; ++i)
for (int j = i + 2; j < n; ++j)
if (x[j] < x[i])
if (maxm[i][j - 1] > x[j]){
int pos = lower_bound(vec[j].begin(), vec[j].end(), x[i]) - vec[j].begin();
if (pos != vec[j].size())
if (vec[j][pos] < maxm[i][j - 1])
return true;
}
return false;
}
int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
b[n - i - 1] = a[i];
}
if (judge(a) || judge(b))printf("YES\n");
else printf("NO\n");
}
return 0;
}