2022.3.5
阿新 • • 發佈:2022-03-07
Codeforces Round #774 (Div. 2) A-C
A
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; const int N=1e6+10,INF=1e8; int a[N]; int main() { int t; scanf("%d", &t); while(t--) { ll n,s; scanf("%lld%lld", &n,&s); n *= n; printf("%lld\n", s / n); } return 0; }
B
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; const int N=2e5+10,INF=1e8; ll a[N]; int main()//紅名大佬的題解 { int t; scanf("%d", &t); while(t--) { int n,f=0; scanf("%d", &n); for (int i = 1; i <= n;i++) { scanf("%lld", &a[i]); } sort(a + 1, a + 1 + n); ll l = 2, r = n, ans = a[n] - a[1] - a[2]; while(l<r) { if(ans>0) { f = 1; break; } ans += a[--r] - a[++l]; } if(f) printf("YES\n"); else printf("NO\n"); } return 0; } /*我的題解 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; const int N=2e5+10,INF=1e8; ll a[N],s[N]; int main() { int t; scanf("%d", &t); while(t--) { int n; ll sum = 0; scanf("%d", &n); for (int i = 1; i <= n;i++) s[i] = 0; for (int i = 1; i <= n ;i++) { scanf("%lld", &a[i]); } sort(a + 1, a + 1 + n); for (int i = 1; i <= n;i++) { s[i] = s[i - 1] + a[i]; } int k = n / 2; if(n%2==0) { int i = n - 1,f=0; for (int j = 2; j <= k + 1; j++) { if(s[n]-s[i]<=s[j]) { if(i>k+1) i--; else { printf("NO\n"); break; } } else { printf("YES\n"); break; } } } else { if(s[n]-s[k+1]>s[k+1]) printf("YES\n"); else printf("NO\n"); } } return 0; } */
C
參考知乎,位運算的使用,對於每個數可以列舉到2的15次方,然後用n=1!+2!+3!...+t表示,最後把t拆成2的幾次冪,即算一下t裡有幾個1加一起取最小值。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<bitset> using namespace std; typedef long long ll; const int N=1e5+10,INF=1e8; ll fac[20]; int main() { int t; ll res = 1; scanf("%d", &t); for (int i = 1; i <= 15;i++) { res *= i; fac[i - 1] = res; } while (t--) { ll n; int ans = 1000; scanf("%lld", &n); for (int i = 0; i < 1 << 15; i++) { bitset<15> b(i); ll t = n; for (int j = 0; j < 15;j++) t -= fac[j] * b[j]; if(t>=0) { bitset<64> b1(t); ans = min(ans,(int)(b1.count() + b.count())); } } printf("%d\n", ans); } return 0; }
藍書
AcWing 91. 最短Hamilton路徑
利用狀態壓縮dp降低空間複雜度,我們定義dp[i][j],其中i為二進位制表示,某一位為1表示已經走過了,0位沒走過,j為當前處於哪個點,那麼我們最後要求的就是dp[(1<<20)-1][n-1],即每一個點都走過了並且已經走到終點的路徑數。需要初始化一下dp[1][0]=0,即當前已經在0點並且走了1個點。於是在任意的時刻,dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+w[k][j]),異或是因為當前轉態和上一個狀態只能有一個點在j,然後對於每一位是1的位k,我們可以由k轉移到j,於是記錄一下轉移的最小值,最後即可得到最優解。當時一直沒太明白本題dp的定義,現在回過頭來看比之前清晰很多。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int w[25][25], a[1<<20][25];
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n;i++)
for (int j = 0; j < n;j++)
scanf("%d", &w[i][j]);
for (int i = 0; i < 1 << n;i++)
for (int j = 0; j < 25;j++)
a[i][j] = INF;
a[1][0] = 0;
for (int i = 0; i < 1 << n; i++)
for (int j = 0; j < n; j++)
{
if (i >> j & 1)
{
for (int k = 0; k < n; k++)
{
if (i - (1 << j) >> k & 1)
a[i][j] = min(a[i][j], a[i-(1<<j)][k] + w[k][j]);
}
}
}
printf("%d\n", a[(1 << n) - 1][n - 1]);
return 0;
}
AcWing 998. 起床困難綜合症
本題是讓我們在[0,m]之間選擇一個數x0,使得經過n次位運算,結果ans最大。那麼我們可以列舉x0的每一位看填1還是0更優。對於某一位k來說,儘量填1,當且僅當滿足,k前面已經填好的更高位加上1<<k不會超過m,並且用第k位進行n次位運算,如果k=1,n次位運算之後仍為1,如果k=0,n次位運算之後仍為0,這時肯定是k=1,更優。於是我們列舉k的每一位後就可以得到最優解
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int n, m, t[N], op[N];
int get(int k,int now)
{
for (int i = 1; i <= n;i++)
{
int x = t[i] >> k & 1;
if(op[i]==1)
now &= x;
else if(op[i]==2)
now |= x;
else if(op[i]==3)
now ^= x;
}
return now;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n;i++)
{
char s[5];
scanf("%s%d", s, &t[i]);
if(s[0]=='A')
op[i] = 1;
else if(s[0]=='O')
op[i] = 2;
else if(s[0]=='X')
op[i] = 3;
}
int res = 0, ans = 0;
for (int i = 29; i >= 0;i--)
{
int res0 = get(i, 0);
int res1 = get(i, 1);
if(res+(1<<i)<=m&&res0<res1)
{
res += 1 << i;
ans += res1 << i;
}
else
ans += res0 <<= i;
}
printf("%d\n", ans);
return 0;
}
AcWing 95. 費解的開關
對於第一行我們可以列舉每一位的狀態,一共是2^5個狀態,當某一位是1時我們就按下當前這一位,注意:這裡列舉的只是第一行按開關的32種方式,和輸入的第一行無關。然後我們固定這一行不動,只是通過該行的下一行來改變它的狀態,於是我們可以列舉2-4行,最後檢查一下如果第5行還有開關是0的話說明該方法是不合法的,在合法的方法裡取最小值得到最優解。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=10,INF=1e8;
int n;
char a[N][N];
int dx[5] ={0,1,-1,0,0},dy[5] = {0, 0, 0, 1, -1};
void change(int x,int y)
{
for(int i=0;i<5;i++)
{
int a1=x+dx[i],b1=y+dy[i];
if(a1>=0&&a1<5&&b1>=0&&b1<5)
{
if(a[a1][b1]=='1') a[a1][b1]='0';
else a[a1][b1]='1';
}
}
}
int slove()
{
int ans = INF;
for (int i = 0; i < 1 << 5;i++)
{
int res = 0,ok=1;
char back[N][N];
memcpy(back, a, sizeof a);
for (int j =0; j < 5;j++)
{
if(i>>j&1)
{
res++;
change(0, j);
}
}
for (int j = 0; j < 4;j++)
{
for (int k = 0; k < 5;k++)
{
if(a[j][k]=='0')
{
res++;
change(j + 1, k);
}
}
}
for (int j = 0; j < 5;j++)
{
if(a[4][j]=='0')
{
ok = 0;
break;
}
}
if(ok) ans=min(ans,res);
memcpy(a, back, sizeof back);
}
if(ans>6)
ans = -1;
return ans;
}
int main()
{
int n;
scanf("%d", &n);
while(n--)
{
for (int i = 0; i < 5; i++)
cin >> a[i];
printf("%d\n", slove());
}
return 0;
}