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->4
和1->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
中的最大值。然后我们发现l
从k
向1
移动的过程中l...r
的最大值只会越来越大,同理r
从k+1
向n
移动的过程也是这的,所以令l=k,r=k+1
才是最优解,边权就是max( a[l] , a[r])
。
然后我们不断的递归链接确定边权就会发现答案就是
#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;
}