20220701 SMU&CUIT友谊赛(题目来源2021ccpc女生赛)

反思其实这套题签到题还是比较多大,而且能做的难题恰好也是我比较擅长的dfs。但是本次比赛中犯的最大的错误就是没有及时看榜,但是别人已经写了三道签到题了我还没有过题

A

这道题的做法比较简单就是正反各跑一遍判断,然后根据判断结果判断答案

#include<bits/stdc++.h>

using namespace std;

const int N = 30;
int n , x , y , m ;
int k[N] , p[N];


int32_t main() {
    cin >> n >> x >> y;
    for( int i = 1 ; i <= n ; i ++ )
        cin >> k[i];
    cin >> m;
    for( int i = 1 ; i <= m ; i ++ )
        cin >> p[i];
    int f1 = 1 , f2 = 1;
    if( x + m > n ) f1 = 0;
    if( x - m < 1 ) f2 = 0;
    for( int i = x + 1 ; i <= x + m && f1 ; i ++ )
        if( p[i-x] != k[i] ) f1 = 0;
    for( int i = x - 1 ; i >= x - m && f2 ; i -- )
        if( p[x-i] != k[i] ) f2 = 0;
    if( f1 && f2 )
        cout << "Unsure\n";
    else if( f1 && y > x )
        cout << "Right\n";
    else if( f2 && y < x )
        cout << "Right\n";
    else
        cout << "Wrong\n";

    return 0;
}

C

正解是状压dp。我通过 dfs+剪枝过掉了这题,并且跑的还算比较快

首先在一条路径上,如果某个公司的商店前后出现了多次,那么在第一次出现的时候领取优惠券是最优的选择。证明可以参考反证法。

在路径 1 -> 2 -> 3 上,1 和 3 属于同一家公司。如果不在 1 选择,就要在 3 选择,对于 3 点来说结果没有影响,但是对于 1 来说,因为不选导致不是最优解

其次第二个性质就是如果有两条路径分别是1->2->3->41->4,对于1,2,3来说只有一条路径,但是对于4来说有两条路径,然后可以发现选择1->2->3->4不会比1->4结果更差,所以1->4这条路径删掉对答案是没哟影响的,依次类推可以把图中这样变都删掉,这样整个图一下子就变成了稀疏图。这个结论是剪枝的关键。

然后根据上述的性质,先把图中的边都删掉,然后dfs 遍历图,加上一点点最优性剪枝就可以把这题过掉了

#include<bits/stdc++.h>

using namespace std;

int read() {
    int x = 0 , ch = getchar();
    while ( ch < '0' || ch > '9') ch = getchar();
    while ( ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x ;
}

const int N = 40;
int n , m , c[N] , w[N] , ans[N];
int g[N][N];
long long cnt[N];
int vis[N];
vector< int > e[N];

void dfs( int x  , int sum , long long t ){ // t 是一个状压剪枝
    vis[ c[x] ] ++ ;
    if( vis[ c[x] ] == 1 ) sum += w[ c[x] ] , t ^= (1<<c[x] );

    if( ans[x] == sum && t == cnt[x] )
    {
        vis[c[x]] --;
        return;
    }
    else if ( sum > ans[x] )
        ans[x] = sum , cnt[x] = t;

    ans[x] = max( sum , ans[x] );
    for( auto it : e[x] )
        dfs( it , sum , t );
    vis[ c[x] ] -- ;
}

int32_t main() {
    n = read() , m = read();
    for( int i = 1 ; i <= n ; i ++ ) c[i] = read();
    for( int i = 1 ; i <= n ; i ++ ) w[i] = read();

    for( int u , v ; m ; m -- )
        u = read() , v = read() , g[u][v] = 1 ;
  
    for( int k = 1 ; k <= n ; k ++ )
        for( int i = 1 ; i < k ; i ++ )
            for( int j = k + 1 ; j <= n ; j ++ )
                if( g[i][j] && g[i][k] && g[k][j] )
                    g[i][j] = 0; // 删边
  
for( int i = 1 ; i <= n ; i ++ ) // 重新建图
    for( int j = i + 1 ; j <= n ; j ++ )
        if( g[i][j] ) e[i].push_back(j);

    dfs( 1 , 0 , 0 );
    for( int i = 1 ; i <= n ; i ++ )
        printf("%d\n" , ans[i] );
    return 0;
}

D

这题很有意思的,假设我们已经链接了n-2条边 就差最后一条边了,现在的情况是1...k 连在一起k+1…n连在一起。所以最后一条边就是从 1...k中选择一个点l,然后从k+1...n中选择一个点r,然后边权就是l..。r中的最大值。然后我们发现lk1移动的过程中l...r的最大值只会越来越大,同理rk+1n移动的过程也是这的,所以令l=k,r=k+1才是最优解,边权就是max( a[l] , a[r])

然后我们不断的递归链接确定边权就会发现答案就是

\[\sum_{i=2}^n max( a_{i-1} , a_i) \]

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5+5;
int a[N] , n , ans;

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    n = read();
    for( int i = 1 ; i <= n ; i ++ )
        a[i] = read();
    for( int i = 2 ; i <= n ; i ++ )
        ans += max( a[i] , a[i-1] );
    cout << ans << endl;
    return 0;
}

G

这道题是小思路,当半径无穷大的时候,每一个圆都可以覆盖整个小镇,所以答案就是\(\frac{1}{n}\)

#include<bits/stdc++.h>

using namespace std;

int main(){
    double a;
    cin >> a;
    printf("%.15llf\n" , 1/a );
}

I

按照题目模拟好了

#include<bits/stdc++.h>
//#define int long long
#define fx ( x + dx[d] )
#define fy ( y + dy[d] )
using namespace std;

const int N = 55;
const int dx[] = { -1 , -1 , 0 , 1 , 1 , 1 , 0 , -1};
const int dy[] = { 0 , 1 , 1 , 1 , 0 , -1 , -1 , -1 };
int n , m , ans;
bool mp[N][N];


int32_t main() {
    int x , y , v = 0  , d = 0;
    string s;
    cin >> n >> m;
    for( int i = 1 ; i <= n ; i ++ )
    {
        cin >> s;
        for( int j = 1 ; j <= n ; j ++ )
        {
            if( s[j-1] == '#' ) mp[i][j] = 1;
            if( s[j-1] == '*' ) x = i , y = j;
        }
    }
    int q;
    cin >> q >> s;
    for( auto it : s ){

        if( it == 'R' ) d = ( d + 1 ) % 8;
        if( it == 'L' ) d = ( d + 7 ) % 8;
        if( it == 'U' ) v ++;
        if( it == 'D' ) v = max(  0 , v - 1 );
        bool flag = 0;
        for( int i = 1 ; i <= v ; i ++ ){
            if( fx < 1 || fy < 1 || fx > n || fy > m ){
                flag = 1;
                v = 0;
                break;
            }
            if( d & 1 ){
                if( ( mp[x][fy] && mp[fx][y] ) ){
                    flag = 1;
                    v = 0;
                    break;
                }
            }
            if( mp[fx][fy] ){
                flag = 1;
                v = 0;
                break;
            }
            x = fx , y = fy;
        }
        cout << ( flag ? "Crash! " : "" ) << x << " " << y << "\n";
    }
    return 0;
}

K

这题其实就是数一下-的数量就好

#include<bits/stdc++.h>

using namespace std;

int32_t main() {
    int n , cnt = 0;
    string s;
    cin >> n;
    getchar();
    for( ; n ; n -- )
    {
        getline( cin , s );
        for( int i = 1 ; i <= 4 ; i ++)
            if( s[i] == '-') cnt ++;
    }
    cout << cnt << endl;
    return 0;
}
posted @ 2022-07-05 16:02  PHarr  阅读(48)  评论(0编辑  收藏  举报