#最短路、gcd、DP# 2017杭州女生赛

18 篇文章 0 订阅
10 篇文章 0 订阅

B. Building Shops

题目链接

Problem Description
HDU’s n classrooms are on a line ,which can be considered as a number line. Each classroom has a coordinate. Now Little Q wants to build several candy shops in these n classrooms.

The total cost consists of two parts. Building a candy shop at classroom i would have some cost ci. For every classroom P without any candy shop, then the distance between P and the rightmost classroom with a candy shop on P’s left side would be included in the cost too. Obviously, if there is a classroom without any candy shop, there must be a candy shop on its left side.

Now Little Q wants to know how to build the candy shops with the minimal cost. Please write a program to help him.

Input
The input contains several test cases, no more than 10 test cases.
In each test case, the first line contains an integer n(1≤n≤3000), denoting the number of the classrooms.
In the following n lines, each line contains two integers xi,ci(−109≤xi,ci≤109), denoting the coordinate of the i-th classroom and the cost of building a candy shop in it.
There are no two classrooms having same coordinate.

Output
For each test case, print a single line containing an integer, denoting the minimal cost.

Sample Input
3
1 2
2 3
3 4
4
1 7
3 1
5 10
6 1

Sample Output
5
11

题目描述:

给出N间教室的位置及在该位置建立糖果店需要的花费C
要在N个教室建糖果店,计算最小总花费,对于每个教室所需要的花费有两种情况:

  1. 建糖果店,则所需要花费为Ci
  2. 不建糖果店,则所需要的花费为这间教室离左边最近的糖果屋的距离

Solution:

用a[i].x来存储第i间教室的位置,a[i].c来存储在第i间教室建糖果屋的花费。

对于每个点有两种可能:建糖果店和不建糖果店,所以用DP求解:
设dp[i][0]为第i间教室不建糖果店所需要花费的最小费用,dp[i][1]表示在第i间教室建立糖果店所需要花费的最小费用。
dp[i][1] = min(dp[i-1][1],dp[i-1][0]) + Ci;
dp[i][0] = dp[j][1] + sum (假设左边距离第i间教室最近的糖果屋为第j间教室,其中sum就为教室 j+1 ~ i 不建糖果屋的费用),糖果屋j的位置需要从当前第i-1间教室开始枚举到第1间教室。如果暴力求解sum的值,那么时间复杂度会变为O(n^3),由于每间教室的位置是递减的,那么就可以用sum += (i - j) * (a[j + 1].x - a[j].x)来记录教室 j+1 ~ i 的费用和:由于位置是递减的,所以当j循环到前一间教室j-1时的sum就等于j时的sum加上 j ~ i 这些教室分别到教室j-1的距离。

复杂度:O(n^2)

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <iostream>
using namespace std;
typedef long long LL;
const int MaxN = 3e3 + 5;
const int Mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;  

LL dp[MaxN][3];

struct node {
    LL x, c;
}a[MaxN];

bool cmp(node a, node b) { return a.x < b.x; }
int main() {
    int n;
    while(~scanf("%d", &n)) {
        for(int i = 1; i <= n; i++) dp[i][0] = dp[i][1] = INF;
        for(int i = 1; i <= n; i++) scanf("%lld %lld", &a[i].x, &a[i].c);
        sort(a + 1, a + n + 1, cmp);
        dp[1][1] = a[1].c;
        for(int i = 2; i <= n; i++) {
            dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]) + a[i].c;
            LL sum = 0LL;
            for(int j = i - 1; j >= 1; j--) {
                sum += (i - j) * (a[j + 1].x - a[j].x);
                dp[i][0] = min(dp[i][0], dp[j][1] + sum);
            }
        }
        printf("%lld\n", min(dp[n][0], dp[n][1]));
    }
    return 0;
}

C. Coprime Sequence

题目链接

Problem Description
Do you know what is called Coprime Sequence”? That is a sequence consists of n positive integers, and the GCD (Greatest Common Divisor) of them is equal to 1. Coprime Sequence” is easy to find because of its restriction. But we can try to maximize the GCD of these integers by removing exactly one integer. Now given a sequence, please maximize the GCD of its elements.

Input
The first line of the input contains an integer T(1≤T≤10), denoting the number of test cases.
In each test case, there is an integer n(3≤n≤100000) in the first line, denoting the number of integers in the sequence.
Then the following line consists of n integers a1,a2,…,an(1≤ai≤109), denoting the elements in the sequence.

Output
For each test case, print a single line containing a single integer, denoting the maximum GCD.

Sample Input
3
3
1 1 1
5
2 2 2 3 2
4
1 2 4 8

Sample Output
1
2
2

题目描述:

给出n个数,可以通过删除任意一个数字使得剩下数字的gcd最大

Solution:

从正向和反向分别存一下起始位置到该位置的gcd值,然后枚举删点
复杂度:O(n)

代码:

#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MaxN = 1e5 + 5;
const int Pt = 1e9 + 7;

int a[MaxN];
int gcd1[MaxN], gcd2[MaxN];

int gcd(int a, int b) {
    if(a < b) return gcd(b, a);
    if(b == 0) return a;
    return gcd(b, a % b);
}
int main() { 
    int T;
    scanf("%d", &T);
    while(T--) {
        int n; 
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        gcd1[1] = a[1], gcd2[n] = a[n];
        //正向求gcd
        for(int i = 2; i <= n; i++) gcd1[i] = gcd(gcd1[i - 1], a[i]);
        //反向求gcd
        for(int i = n - 1; i >= 1; i--) gcd2[i] = gcd(gcd2[i + 1], a[i]);
        gcd1[0] = gcd2[n + 1] = 0;
        int ans = gcd1[n];
        for(int i = 1; i <= n; i++) 
            ans = max(ans, gcd(gcd1[i - 1], gcd2[i + 1]));
        printf("%d\n", ans);
    }
    return 0;
}

D. Deleting Edges

题目链接

Deleting Edges
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1154 Accepted Submission(s): 407

Problem Description
Little Q is crazy about graph theory, and now he creates a game about graphs and trees.
There is a bi-directional graph with nodes, labeled from 0 to . Every edge has its length, which is a positive integer ranged from 1 to 9.
Now, Little Q wants to delete some edges (or delete nothing) in the graph to get a new graph, which satisfies the following requirements:
(1) The new graph is a tree with edges.
(2) For every vertice , the distance between 0 and on the tree is equal to the length of shortest path from 0 to in the original graph.
Little Q wonders the number of ways to delete edges to get such a satisfied graph. If there exists an edge between two nodes and , while in another graph there isn’t such edge, then we regard the two graphs different.
Since the answer may be very large, please print the answer modulo .

Input
The input contains several test cases, no more than 10 test cases.
In each test case, the first line contains an integer , denoting the number of nodes in the graph.
In the following lines, every line contains a string with characters. These strings describes the adjacency matrix of the graph. Suppose the -th number of the -th line is , if is a positive integer, there is an edge between and with length of , if , then there isn’t any edge between and .
The input data ensure that the -th number of the -th line is always 0, and the -th number of the -th line is always equal to the -th number of the -th line.

Output
For each test case, print a single line containing a single integer, denoting the answer modulo .

Sample Input
2 01 10 4 0123 1012 2101 3210

Sample Output
1 6

题目描述

给出一个邻接矩阵,要求删除一条边后分别不改变0点到图中其余点的最短路,求一共有多少种删除方法

Solution:

这是一道最短路的变形思维题。如果删除掉一条边,而使整张图的最短路不变,那么这条路径是对整张图的最短路没有贡献,也就是这条路径跟原有的最短路径重合。对于每个点,处理出重复路径数目后累乘。

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <iostream>
using namespace std;
typedef long long LL;
const int MaxN = 2e5 + 5;
const int Mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

LL mp[55][55];
LL dist[55][55], cnt[55];
int n;

void Floyd() {
    for(int i = 0; i < n; i++) 
        for(int j = 0; j < n; j++) {
            if(mp[i][j] == 0) dist[i][j] = INF;
            else dist[i][j] = mp[i][j];
        }
    for(int k = 0; k < n; k++)
        for(int i = 0; i < n; i++) 
            for(int j = 0; j < n; j++) 
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
int main() {
    while(~scanf("%d", &n)) {
        memset(cnt, 0, sizeof(cnt));
        for(int i = 0; i < n; i++) 
            for(int j = 0; j < n; j++) 
                scanf("%1lld", &mp[i][j]);
        Floyd();
        dist[0][0] = 0;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                if(!mp[j][i] || i == j || dist[j][i] == INF) continue;
                if(dist[0][j] + mp[j][i] == dist[0][i]) cnt[i]++;
            }
        }
        LL ans = 1LL;
        for(int i = 1; i < n; i++) ans = (ans * cnt[i]) % Mod;
        printf("%d\n", ans);
    }
    return 0;
}

G. Graph Theory

题目链接

Problem Description
Little Q loves playing with different kinds of graphs very much. One day he thought about an interesting category of graphs called “Cool Graph”, which are generated in the following way:
Let the set of vertices be {1, 2, 3, …, n}. You have to consider every vertice from left to right (i.e. from vertice 2 to n). At vertice i, you must make one of the following two decisions:
(1) Add edges between this vertex and all the previous vertices (i.e. from vertex 1 to i−1).
(2) Not add any edge between this vertex and any of the previous vertices.
In the mathematical discipline of graph theory, a matching in a graph is a set of edges without common vertices. A perfect matching is a matching that each vertice is covered by an edge in the set.
Now Little Q is interested in checking whether a ”Cool Graph” has perfect matching. Please write a program to help him.

Input
The first line of the input contains an integer T(1≤T≤50), denoting the number of test cases.
In each test case, there is an integer n(2≤n≤100000) in the first line, denoting the number of vertices of the graph.
The following line contains n−1 integers a2,a3,…,an(1≤ai≤2), denoting the decision on each vertice.

Output
For each test case, output a string in the first line. If the graph has perfect matching, output ”Yes”, otherwise output ”No”.

Sample Input
3
2
1
2
2
4
1 1 2
Sample Output
Yes
No
No

题目描述:

给定图的 n 个顶点以及 a2, a3, …, an,ai代表两种操作中的一种:

  1. 在点 i 与所有标号小于 i 的点间建一条边
  2. 点 i 与所有标号小于 i 的点都不存在边

如果每个顶点有且仅有一条边与之相连就称为完美匹配,问是否存在完美匹配。

Solution:

题目的关键在于处理a[i] = 2 的点,若要使这些点一定有边与之相连,则要保证该点后面一定有相应数量的a[i] = 1 的点与之匹配,若已经处理了所有 a[i] = 2 的点的边的情况,则剩下的所有的点必然能够两两组合

代码:

#include <bits/stdc++.h>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int MaxN = 1e5 + 5;

int a[MaxN];

int main() {
    int n, t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 1; i < n; i++) scanf("%d", &a[i]);
        if(n % 2) {
            printf("No\n");
            continue;
        }
        int cnt1 = 0, cnt2 = 0, flag = 0;
        for(int i = n - 1; i >= 1; i--) {
            if(a[i] == 1) cnt1++;
            else cnt2++;
            if(cnt2 > cnt1) {
                printf("No\n");
                flag = 1;
                break;
            }
        }
        if(flag) continue;
        printf("Yes\n");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值