程序员进阶之算法练习(八十)

题目1

题目链接
题目大意:
有一个数组a,仅有整数1和-1组成,我们定义数组a的乘积为:
对于 1≤𝑖<𝑗≤𝑛, 𝑎[𝑖]⋅𝑎[𝑗]=1的数量。

现在给出数组长度n和乘积k,问是否可以构造一个满足要求的数组a;

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤20000)
每个样例1行, n和𝑘,表示数组长度和乘积k (2≤𝑛≤100 ; 0≤𝑘≤(𝑛−1)*𝑛/2)

输出:
如果有解,则输出YES,然后第二行输出n个整数;
如果无解,则输出NO;

Examples
input
7
2 0
2 1
3 1
3 2
3 3
5 4
5 5

output
YES
1 -1
YES
1 1
YES
1 -1 1
NO
YES
1 1 1
YES
-1 1 -1 1 1
NO

题目解析:
题目给出的数据范围比较小,可以直接枚举;
遍历数组存在0个、1个、2个、、、到n个1的情况,剩下用-1填充;
然后再遍历整个数组,两两计算a[i]*a[j] = 1的数量;
如果满足要求则输出;

class Solution {
    static const int N = 201010;
    char s[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, k;
            cin >> n >> k;
            int ans = 0;
            for (int i = 0; i <= n; ++i) //第i个开始都是1,前面都是-1
            {
                int sum = 0;
                for (int x = 0; x < n; ++x) {
                    for (int y = x + 1; y < n; ++y) {
                        if ((x < i && y < i) || (x >= i && y >= i)) {
                            ++sum;
                        }
                    }
                }
                if (sum == k) {
                    ans = 1;
                    int tmp = 0;
                    cout << "YES" << endl;
                    while (tmp < i) {
                        cout << "-1" << " ";
                        ++tmp;
                    }
                    while (tmp < n) {
                        cout << "1" << " ";
                        ++tmp;
                    }
                    cout << endl;
                    break;
                }
            }
            if (!ans) cout << "NO" << endl;
            
        }
    }
}

题目2

题目链接
题目大意:
给出1到n的整数排列所形成的数组p以及整数k,现在可以对数组进行下列操作形成从小到大的数组:
选择任意两个相差为k的位置,交换他们的位置上的元素;
比如说数组[3,2,1] 和 k=2,那么可以选择位置1和位置3进行交换,得到数组[1,2,3],满足题目要求;

但是有些数组的无法满足要求,比如说[2,4,3,1]和k=2,此时无法通过交换得到数组[1,2,3,4];
这种情况下,此时允许在最初的时候(进行交换操作之前),对选择任意数组两个位置,进行交换(该交换只允许一次),比如说:
数组[2,4,3,1]选择整数1和2交换得到[1,4,3,2],然后再进行交换操作,可以得到从小到大的数组[1,2,3,4];

现在的任务是给出数组p和整数k,问是否能得到从小到大的数组。

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
每个样例2行
第一行 n和𝑘,表示数组长度和整数k (2≤𝑛≤2⋅1e5; 1≤𝑘≤𝑛−1)
第二行 n个整数 𝑝1,𝑝2,…,𝑝𝑛 (1≤𝑝𝑖≤n)

输出:
每个样例一行,输出0/1/-1,分别表示:
0,有解,并且不需要提前交换;
1,有解,但是必须要提交交换;
-1,无解;

Examples
input
6
4 1
3 1 2 4
4 2
3 4 1 2
4 2
3 1 4 2
10 3
4 5 9 1 8 6 10 2 3 7
10 3
4 6 9 1 8 5 10 2 3 7
10 3
4 6 9 1 8 5 10 3 2 7

output
0
0
1
0
1
-1

题目解析:
当k=1的时候,肯定有解,因为可以随意交换;
当k>1的时候,每个位置能和相距为k的位置交换,那么将距离为k的元素全部提取出来,这部分元素就能任意交换,比如说数组[1,2,3,4,5,6,7]
k=2时,数组可以拆分为[1,3,5,7]和[2,4,6],这两个数组的元素就能任意交换;
k=3时,整数可以拆分为[1,4,7], [2,5], [3,6] 这样三个数组;
我们将数组p,拆分成k个数组,每个数组如果都按照上述的规律展示,那么不需要做提前交换,就可以有解;

通过不匹配当前数组的元素数量,如果为2,那么通过提前交换就有解;如果为其他值则无解;

举个例子,假设数组4 1 3 5 2 6 7,我们拆分得到
457
12
36

那么可以得到4应该在第二组,而不是第一组;
1不应该在第二组,而是应该在第一组;
此时提前交换1和4,有解;

class Solution {
    static const int N = 201010;
    int a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, k;
            cin >> n >> k;
            for (int i = 1; i <= n; ++i) cin >> a[i];
            if (k == 1) cout << 0 << endl;
            else {
                int cnt = 0;
                for (int i = 1; i <= k; ++i) {
                    int idx = i;
                    while (idx <= n) {
                        if ((a[idx] - i) % k) ++cnt;
                        idx += k;
                    }
                }
                if (cnt == 2) cnt = 1;
                cout << (cnt < 2 ? cnt : -1) << endl;
            }
        }
    }
}

题目3

题目链接
题目大意:
有n个整数的数组a,数组元素由1和-1组成;
现在可以对数组中的元素进行操作(最后一个元素无法操作),将这个元素与右边元素进行符号反转;
比如说数组[1, -1, -1],如果我们选择[1, -1]进行符号反转,则得到[-1, 1, -1];如果我们选择[-1, -1]进行符号反转,则可以得到[1, 1, 1];
这个操作必须执行一次,问操作完数组最大的和是多少。(a1+a2+a3...+an)

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤500)
每个样例2行,第一行整数𝑛 (2≤𝑛≤10e5)
第二行n个整数 𝑎1,𝑎2,…,𝑎𝑛 (𝑎𝑖=1 or 𝑎𝑖=−1)

输出:
每个样例一行,输出可能的最大数组和;

Examples
input
4
5
-1 1 1 -1 -1
5
1 1 -1 -1 -1
2
1 1
4
1 -1 -1 1

output
3
3
-2
4

题目解析:
直接累加数组的整数,得到整数和sum;
接着遍历数组:
如果能找到两个-1相邻,则直接反转着两个整数,sum=sum+4;
如果能有一个-1,则sum=sum;
如果都是1,则sum=sum - 4;

class Solution {
    static const int N = 201010;
    int a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            int sum = 0, done = 0;
            for (int i = 0; i < n; ++i) {
                cin >> a[i];
                sum += a[i];
            }
            for (int i = 1; i < n && !done; ++i) {
                if (a[i] + a[i - 1] == -2) {
                    sum += 4;
                    done = 1;
                }
            }
            for (int i = 0; i < n && !done; ++i) {
                if (a[i] == -1) {
                    done = 1;
                }
            }
            if (!done) {
                sum -= 4;
            }
            cout << sum << endl;
        }
    }
}

题目4

题目链接
题目大意:
给出一个由 _^ 两种字符组成的字符串,现在小明可以往字符串任意位置插入这两种字符,要求:
1、任何位置的字符,都可以和相邻连续字符组成^_^ 或者 ^^ 这样的字符串;
2、尽可能少的插入字符;

比如说字符串^^__^_^^__^,在第3,4,9,10,11个字符,就无法和相邻连续字符组成满足要求的字符串。

问最少需要插入多少个字符。

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤100)
每个样例1行,字符串𝑠 (1≤|𝑠|≤100)

输出:
每个样例一行,输出最少插入数量;

Examples
input

 7
 ^______^
 ___^_^^^_^___^
 ^_
 ^
 ^_^^^^^_^_^^
 ___^^
 _

output

 5
 5
 1
 1
 0
 3
 2

题目解析:

字符串只包含两种字符,由于可以插入 ^ 字符,^字符可以和_组成^_^ ,也可以和^组成^_^,那么必然题目有解;
 对于题目来说,单个^,以及_左右不是^都是不合法的,需要插入^;
 那么就可以有简单的解决方案:
 从左到右遍历字符串,对于第i个字符串s[i],假如:
 1、s[i]是^字符,只要i>0,^必然合法,因为s[i-1]是'^',已经合法;如果s[i-1]是'_',那么在处理s[i-1]的时候已经做了合法处理;
 2、s[i]是_字符,如果i==0,需要在前面插入^,否则只需要考虑后面是_的情况,需要插入一个^;
class Solution {
    static const int N = 201010;
    char s[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> s;
            int n = strlen(s);
        
            int ans = 0;
            for (int i = 0; i < n; ++i) {
                if (s[i] == '_') {
                    if (i == 0) {
                        ++ans;
                    }
                    if (i + 1 == n || s[i + 1] == '_') {
                        ++ans;
                    }
                }
                else {
                    if (n == 1) {
                        ++ans;
                    }
                }
            }
            cout << ans << endl;
            
        }
    }
}

题目5

题目链接
题目大意:
给出一个0/1字符组成的字符串,现在按照以下规则进行排序:
1、将字符串str作为矩阵第一行;
2、将字符串str所有字符右移1位(最后一位字符会移动到最左边的位置),将这个字符当做下一行;
重复以上规则,直到得到一个正方形矩阵。
以“101”字符串为例:
第一行是101;
第二行是110;
第三行是011;

问得到的正方形矩阵中,由1组成的连续字符矩阵最大面积是多少。

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤2⋅1e4 )
每个样例1行,字符串𝑠 (1≤|𝑠|≤2⋅1e5),

输出:
每个样例一行,输出最大的面积,如果不存在则输出0;

Examples
input
5
0
1
101
011110
101010

output
0
1
2
6
1

题目解析:
由于字符串长度很大,暴力枚举计算的空间复杂度太高;
分析题目给出的构造规则,我们发现0会形成一条斜线,将矩形分割开来;

参考题目样例4,我们可以知道最终矩阵:
0 0 1 1 1 1 0
1 0 0 1 1 1 1
1 1 0 0 1 1 1
1 1 1 0 0 1 1
1 1 1 1 0 0 1
0 1 1 1 1 0 0
存在一个长为4,等边三角形;

同理,这样的字符串,一样在矩阵中存在长为4的等边三角形。
1 1 1 1 0 0 0
0 1 1 1 1 0 0
0 0 1 1 1 1 0
0 0 0 1 1 1 1
1 0 0 0 1 1 1
1 1 0 0 0 1 1

极端的情况,连续的1拆分在两边
1 1 0 0 0 1 1
1 1 1 0 0 0 1
1 1 1 1 0 0 0
0 1 1 1 1 0 0
0 0 1 1 1 1 0
0 0 0 1 1 1 1
1 0 0 0 1 1 1

1 1 1 0 0 0 1
1 1 1 1 0 0 0
0 1 1 1 1 0 0
0 0 1 1 1 1 0
0 0 0 1 1 1 1
1 0 0 0 1 1 1
1 1 0 0 0 1 1

可以得到一个规律,我们根据字符串中“连续”1的数量,就可以得到等边三角形的数量。
这个连续1的计算方式,可以用下面的规则:
将字符串1100011,复制一遍粘到末尾 1100011+1100011 = 11000111100011
这样去计算一遍连续的最长字符串即可。

注意:
1、如果全为1,答案为n * n;
2、如果全为0,答案为0;
3、结果有可能超int32,需要用long long。

class Solution {
    static const int N = 401010;
    char s[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> s;
            int n = strlen(s);
            for (int i = 1; i < n; ++i) {
                s[n + i - 1] = s[i - 1]; // 拼接
            }
            s[n * 2 - 1] = '0';
            int cur = 0, zero = 0, len = 0;
            for (int i = 0; i < 2 * n; ++i) {
                if (s[i] == '0') {
                    len = max(len, cur);
                    cur = 0;
                }
                else {
                    ++cur;
                }
            }
            for (int i = 0; i < n; ++i) zero |= (s[i] == '0');
            if (!zero) cout << n * 1LL * n << endl;
            else {
                if (len <= 2) cout << len << endl;
                else if (len % 2) cout << (len + 1) / 2LL * (len + 1) / 2 << endl;
                else cout << len / 2LL * (len + 2) / 2 << endl;
            }
            
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356

推荐阅读更多精彩内容