2019ICPC南京【个人题解ACHJK】

A - A Hard Problem(找规律)

思路

打表之后,可以得出一个答案的通项公式 ( n + 1 ) / 2 + 1 (n+1)/2+1 (n+1)/2+1

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t; cin >> t;
    while(t --)
    {
        int n; scanf("%d", &n);
        printf("%d\n", (n + 1) / 2 + 1);
    }
    return 0;
}

C - Digital Path(dp、记忆化搜索)

思路

显然是记忆化搜索的题(经典滑雪),很容易定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示以位置 ( i , j ) (i, j) (i,j)结尾的最长长度为 k k k的路径数量。但是会爆空间,发现当路径长度大于3之后,路径长度是多少对答案并没有影响,因此可以统一到一起。换一下 k k k的定义,当 k = 1 , 2 , 3 k=1,2,3 k=1,2,3 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]就表示以位置 ( i , j ) (i, j) (i,j)结尾的最长长度为 k k k的路径数量;当 k = 4 k=4 k=4 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]就表示以位置 ( i , j ) (i, j) (i,j)结尾的最长长度大于等于 k k k的路径数量;
d p [ i ] [ j ] [ k ] = ∑ d p [ n i ] [ n j ] [ k − 1 ] , 2 < = k < = 4 ;   d p [ i ] [ j ] [ 4 ] = ∑ d p [ n i ] [ n j ] [ 4 ] dp[i][j][k] = \sum dp[ni][nj][k-1], 2<= k <= 4;\ dp[i][j][4] = \sum dp[ni][nj][4] dp[i][j][k]=dp[ni][nj][k1],2<=k<=4; dp[i][j][4]=dp[ni][nj][4]其中 ( n i , n j ) (ni, nj) (ni,nj) ( i , j ) (i,j) (i,j)的相邻位置且 a [ n i ] [ n j ] = a [ i ] [ j ] − 1 a[ni][nj] = a[i][j] - 1 a[ni][nj]=a[i][j]1
然后还需要考虑特殊情况:当 d p [ i ] [ j ] [ 2 / 3 / 4 ] dp[i][j][2/3/4] dp[i][j][2/3/4]任意一个不为0,需要将 d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]设置为0,因为到位置 ( i , j ) (i,j) (i,j)的最长路必然不可能是长度1的了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
int a[N][N];
int dp[N][N][5];
int n, m;
bool is_max(int x, int y)
{
    for(int i = 0; i < 4; i ++)
    {
        int nx = x + dx[i], ny = y + dy[i];
        if(nx < 1 || ny < 1 || nx > n || ny > m) continue;
        if(a[nx][ny] == a[x][y] + 1) return false;
    }
    return true;
}
void dfs(int x, int y)
{
    if(dp[x][y][1] != -1) return;
    dp[x][y][1] = 1;
    dp[x][y][2] = dp[x][y][3] = dp[x][y][4] = 0;
    for(int i = 0; i < 4; i ++)
    {
        int nx = x + dx[i], ny = y + dy[i];
        if(nx < 1 || ny < 1 || nx > n || ny > m) continue;
        if(a[nx][ny] == a[x][y] - 1)
        {
            dfs(nx, ny);
            if(dp[nx][ny][4] > 0) dp[x][y][4] = (dp[x][y][4] + dp[nx][ny][4]) % mod;
            for(int j = 4; j >= 2; j --)
            {
                if(dp[nx][ny][j - 1] > 0) 
                    dp[x][y][j] = (dp[x][y][j] + dp[nx][ny][j - 1]) % mod;
            } 
            
        }
    }
    if (dp[x][y][2] > 0 || dp[x][y][3] > 0 || dp[x][y][4] > 0) dp[x][y][1] = 0;
    // for(int i = 1; i <= 4; i ++) cout << x << ' ' << y << ' ' << a[x][y] << ' ' << i << ' ' << ' ' << dp[x][y][i] << endl;
}
int main()
{
    memset(dp, -1, sizeof dp);
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) scanf("%d", &a[i][j]);
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            if(dp[i][j][1] == -1) dfs(i, j);
    int ans = 0;
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j ++)
        {
            if(is_max(i, j) && dp[i][j][4] > 0) ans = (ans + dp[i][j][4]) % mod;
        }
    }
    printf("%d\n", ans);
    return 0;
}

H - Prince and Princess(思维)

思路

题意很迷惑,没有说王子知不知道每种人物的数量。
于是假设如果王子不知道,那么只要存在说假话与真假不定的人,那么就会输出NO,那这题就没意义了。所以王子知道每种人的数量。
然后呢,考虑以下

  1. 三个问题王子只选“公主在哪个房间?”这个问题去问,其他的问题没有什么意义
  2. 考虑说真话的人数 a a a多于【说假话和随便说的人】的和 b + c b + c b+c,那么王子问完每个人“公主在哪个房间?”至少会有 a a a个人说的房间是相同的,相同房间的数量也可能多于 a a a,因为 c c c个随便说的人也可能说是真正的那个房间。
  3. 再考虑特殊情况: a = 10 , b = 1 , c = 1 a=10, b=1, c=1 a=10,b=1,c=1,其实不用询问所有的人,而只需要询问5次就可获得至少3次相同的房间,显然说出3个相同房间的人必然都是说真话的人,因为说假话和随便说的人都没3个人。因此当 a > b + c a>b+c a>b+c,只需要询问 2 ( b + c ) + 1 2(b+c)+1 2(b+c)+1次就可获得真正的房间。
  4. 最后的极端情况:只有一个房间,一次询问都不需要,公主必然在该房间内。

代码

#include <iostream>

using namespace std;

int main()
{
    int a, b, c;
    cin >> a >> b >> c;
    int d = (b + c) * 2 + 1;
    if (a == 1 && b == 0 && c == 0)
    {
        cout << "YES\n0\n";
        return 0;
    }
    if (a + b + c < d) cout << "NO" << endl;
    else cout << "YES\n" << d << endl;
    return 0;
}

J - Spy(期望、二分图最大权匹配)

思路

题意比较绕,大概是这样,给你大小为 n n n的四个数组, a , p , b , c a,p,b,c a,p,b,c,你可以将 b , c b,c b,c任意两两匹配,匹配完加起来成为数组 d d d,然后将 a a a d d d随机匹配,如果 a [ i ] < d [ i ] a[i] < d[i] a[i]<d[i],则会获得 p [ i ] p[i] p[i]的金币,把所有随机情况都求一遍获得的金币总数,可以得到一个金币总数期望。现在让你找到一个 b , c b,c b,c匹配,使得该匹配获得的金币总数期望最大,输出该期望*n作为答案。
由于是两两匹配,往二分图匹配的方向考虑。考虑 b i b_i bi c j c_j cj匹配之后会对期望贡献多少,设 d = b i + c j d=b_i+c_j d=bi+cj,则在与 a a a数组的所有匹配情况中, d d d a k a_k ak匹配到的概率为 ( n − 1 ) ! n ! = 1 n \frac{(n-1)!}{n!}=\frac{1}{n} n!(n1)!=n1,贡献的期望为 1 n ∗ p k [ d > a k ] \frac{1}{n}*p_k[d > a_k] n1pk[d>ak]。又因为最后算出的期望需要乘上n,所以贡献的期望为 p k [ d > a k ] p_k[d>a_k] pk[d>ak]
那么对于每一对 b i b_i bi c j c_j cj,我们枚举 a k a_k ak,然后将 b i + c j > a k b_i + c_j > a_k bi+cj>ak p k p_k pk加到 b i b_i bi c j c_j cj的边权上即可。
最后用KM做一遍二分图最大匹配就可以啦!

代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 4E2 + 10;
int w[N][N];
int la[N], lb[N];
bool va[N], vb[N];
int mat[N];
int n, delta, upd[N], last[N];
LL a[N], b[N], c[N];
int p[N];
bool dfs(int x, int fa)
{
    va[x] = 1;
    for(int y = 1; y <= n; y ++)
        if(!vb[y])
        {
            if(la[x] + lb[y] - w[x][y] == 0)
            {
                vb[y] = 1,last[y] = fa;
                if(!mat[y] || dfs(mat[y], y))
                {
                    mat[y] = x;
                    return true;
                }
            }
            else if(upd[y] > la[x] + lb[y] - w[x][y])
            {
                upd[y] = la[x] + lb[y] - w[x][y];
                last[y] = fa;
            }
        }
    return false;
}
LL km()
{
    for(int i = 1;  i<= n; i ++)
    {
        la[i] = -(1 << 30); lb[i] = 0;
        for(int j = 1; j <= n; j ++)
            la[i] = max(la[i], w[i][j]);
    }
    for(int i = 1; i <= n; i ++)
    {
        memset(va, 0, sizeof va), memset(vb, 0, sizeof vb);
        memset(last, 0, sizeof last), memset(upd, 0x7f, sizeof upd);
        int st = 0; mat[0] = i;
        while(mat[st])
        {
            LL delta = 1ll << 60;
            if(dfs(mat[st], st)) break;
            for(int j = 1; j <= n; j ++)
                if(!vb[j] && upd[j] < delta)
                {
                    delta = upd[j];
                    st = j;
                }
            for(int j = 1; j <= n; j ++)
            {
                if(va[j]) la[j] -= delta;
                if(vb[j]) lb[j] += delta;
                else upd[j] -= delta;
            }
            vb[st] = 1;
        }
        while(st)
        {
            mat[st] = mat[last[st]];
            st = last[st];
        }
    }
    LL ans = 0;
    for(int i = 1; i <= n; i ++) ans += w[mat[i]][i];
    return ans;
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= n; i ++) cin >> p[i];
    for(int i = 1; i <= n; i ++) cin >> b[i];
    for(int i = 1; i <= n; i ++) cin >> c[i];
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= n; j ++)
            for(int k = 1; k <= n; k ++)
                if(a[k] < b[i] + c[j]) w[i][j] += p[k];
    }

    cout << km() << endl;
    return 0;
}

K - Triangle(简单计算几何)

思路

(一开始没用计算几何板子wa🤮了,之后用了个板子就过了)
首先,判断一下点是否在三角形边上,不是的话输出-1。
然后,我特判了点在三角形顶点与三角形某一边中点的情况。
最后,直接分类讨论,把点在三角形六个区域的情况都考虑一下即可。比如下面这种情况:
在这里插入图片描述
假设 P P P为题目给定的点(在 B C BC BC中点右边),那么所求的点必然在 A B AB AB上(假设为 D D D)。那么有:
1 2 S A B C = S B D P \frac{1}{2}S_{ABC}=S_{BDP} 21SABC=SBDP
故:
1 2 1 2 A B ∗ B C ∗ s i n B = 1 2 B D ∗ B P ∗ s i n B \frac{1}{2}\frac{1}{2}AB*BC*sinB=\frac{1}{2}BD*BP*sinB 2121ABBCsinB=21BDBPsinB
于是:
B D A B = 1 2 B C B P \frac{BD}{AB} = \frac{1}{2}\frac{BC}{BP} ABBD=21BPBC
于是可以求出 B D BD BD A B AB AB的比值,于是就可以利用 A B AB AB向量求出 B D BD BD向量进而求出 D D D点。
其他的情况也是类似滴。
(代码很丑建议别看)

代码

#include <iostream>
#include <cmath>

using namespace std;

const double eps = 1e-8;

int sgn(double x)
{
    if (fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}

struct Point
{
    double x, y;
    Point(){}
    Point(double x, double y):x(x), y(y){}
    Point operator - (const Point& b) const
    {
        return Point(x - b.x, y - b.y);
    }
}p[5];

struct Line
{
    Point p1, p2;
    Line(){}
    Line(Point p1, Point p2):p1(p1), p2(p2){}
}l[5];

double Dist(Point a, Point b)
{
    double dx = a.x - b.x, dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

double Dot(Point a, Point b)
{
    return a.x * b.x + a.y * b.y;
}

double Cross(Point a, Point b)
{
    return a.x * b.y - a.y * b.x;
}

bool Point_on_Line(Point P, Line L)
{
    return sgn(Cross(P - L.p1, L.p2 - L.p1)) == 0 && sgn(Dot(P - L.p1, P - L.p2)) <= 0;
}

double getmid(double a, double b)
{
    return (a + b) / 2;
}

struct Node
{
    Point p1, p2;
    double d;
};

Node solve()
{
    Node res;

    if (Point_on_Line(p[4], l[1]))
    {
        if (Dist(p[1], p[4]) < Dist(p[2], p[4]))
        {
            res.p1 = p[2], res.p2 = p[3];
            res.d = Dist(p[1], p[2]) / Dist(p[4], p[2]) / 2;
        }
        else  
        {
            res.p1 = p[1], res.p2 = p[3];
            res.d = Dist(p[1], p[2]) / Dist(p[4], p[1]) / 2;
        }
    }
    else if(Point_on_Line(p[4], l[3]))
    {
        if (Dist(p[3], p[4]) < Dist(p[1], p[4]))
        {
            res.p1 = p[1], res.p2 = p[2];
            res.d = Dist(p[1], p[3]) / Dist(p[4], p[1]) / 2;
        }
        else  
        {
            res.p1 = p[3], res.p2 = p[2];
            res.d = Dist(p[1], p[3]) / Dist(p[4], p[3]) / 2;
        }
    }
    else 
    {
        if (Dist(p[2], p[4]) < Dist(p[3], p[4]))
        {
            res.p1 = p[3], res.p2 = p[1];
            res.d = Dist(p[2], p[3]) / Dist(p[4], p[3]) / 2;
        }
        else  
        {
            res.p1 = p[2], res.p2 = p[1];
            res.d = Dist(p[2], p[3]) / Dist(p[4], p[2]) / 2;
        }
    }

    return res;
}

int main()
{
    int _;
    scanf("%d", &_);
    while (_ --)
    {
        for (int i = 1; i <= 4; i ++ )
            scanf("%lf%lf", &p[i].x, &p[i].y);
        if (p[1].x == p[4].x && p[1].y == p[4].y)
        {
            printf("%.10f %.10f\n", getmid(p[2].x, p[3].x), getmid(p[2].y, p[3].y));
            continue;
        }
        if (p[2].x == p[4].x && p[2].y == p[4].y)
        {
            printf("%.10f %.10f\n", getmid(p[1].x, p[3].x), getmid(p[1].y, p[3].y));
            continue;
        }
        if (p[3].x == p[4].x && p[3].y == p[4].y)
        {
            printf("%.10f %.10f\n", getmid(p[1].x, p[2].x), getmid(p[1].y, p[2].y));
            continue;
        }

        l[1] = Line(p[1], p[2]);
        l[2] = Line(p[2], p[3]);
        l[3] = Line(p[3], p[1]);

        if (!Point_on_Line(p[4], l[1]) && !Point_on_Line(p[4], l[2]) && !Point_on_Line(p[4], l[3]))
        {
            puts("-1");
            continue;
        }

        if (Point_on_Line(p[4], l[1]) && p[4].x == getmid(p[1].x, p[2].x) && p[4].y == getmid(p[1].y, p[2].y))
        {
            printf("%.10f %.10f\n", p[3].x, p[3].y);
            continue;
        }
        if (Point_on_Line(p[4], l[2]) && p[4].x == getmid(p[3].x, p[2].x) && p[4].y == getmid(p[3].y, p[2].y))
        {
            printf("%.10f %.10f\n", p[1].x, p[1].y);
            continue;
        }
        if (Point_on_Line(p[4], l[3]) && p[4].x == getmid(p[1].x, p[3].x) && p[4].y == getmid(p[1].y, p[3].y))
        {
            printf("%.10f %.10f\n", p[2].x, p[2].y);
            continue;
        }
        
        Node res = solve();

        Point p1 = res.p1, p2 = res.p2;
        double a = res.d;

        printf("%.10f %.10f\n", a * (p2 - p1).x + p1.x, a * (p2 - p1).y + p1.y);

    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值