1002-Time-division Multiplexing
題意:
給定你\(n\)個長度小於\(12\)的字符串,然后循環的構造一個無限長都的字符串\(s\),構造方式為依次從\(1-n\)的串中取出一個一個字符,如兩個字符串長度為\(3,2\),那么\(s\) = \(t_{11}t_{21}t_{12}t_{22}t_{13}t_{21}t_{11}t_{22}t_{12}t_{21}t_{13}t_{22}\ \ \ t_{11}t_{21}\),問你能包含\(n\)個字符串中出現的所有字符的最小長度的子串長度為多少
思路:
會發現經過某個長度后\(s\)就會重復,那么這個長度是多少呢,手寫幾個例子會發現等於\(n\)個字符串的\(LCM*n\),但是這樣考慮還不行,因為串是無限長度,所以需要把串長再乘\(2\)才會得到不遺漏的\(s\)的一個模式串,然后我們就只需要在循環節長度\(*2\)的這個串上做尺取就可以得到我們想要的答案了。
復雜度因為\(lcm(2,3,4,7,8,9,10,11,12) = 27720\),所以串長最多也就是\(5.5e6\),再加上雙指針\(O(n)\)的掃一遍,時間正好。
被\(hdoj\)惡心壞了,寫完了交不上去......
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 2e7 + 10;
char s[N];
char t[110][20];
int len[110];
int n,cnt[30],tot,vis[30];
int gcd(int a,int b) {
return b == 0 ? a : gcd(b,a%b);
}
void solve() {
tot = 0;
for(int i = 0;i < 26;i ++) vis[i] = cnt[i] = 0;
scanf("%d",&n);
getchar();
for(int i = 1;i <= n;i ++) {
scanf("%s",t[i]+1);
len[i] = strlen(t[i]+1);
}
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= len[i];j ++) {
if(!vis[t[i][j]-'a']) {
vis[t[i][j]-'a'] = 1;
tot++;
}
}
}
int lcm = len[1];
for(int i = 2;i <= n;i ++) {
lcm = lcm * len[i] / gcd(lcm,len[i]);
}
lcm *= n;
int id = 1,round = 1;
for(int i = 1;i <= lcm;i ++) {
if(id > n) {
id = 1; round++;
}
s[i] = t[id][(round-1)%len[id]+1];
id++;
}
for(int i = lcm + 1;i <= lcm * 2;i ++) {
s[i] = s[i-lcm];
}
lcm *= 2;
s[lcm+1] = '\0';
int mi = INF;
int now = 0;
for(int st = 1,ed = 1;st <= lcm;) {
if(ed <= lcm && now < tot) {
while(ed <= lcm && now < tot) {
if(cnt[s[ed]-'a'] == 0) {
now++;
}
cnt[s[ed]-'a']++;
ed++;
}
}
if(now == tot) {
mi = min(mi,ed - st);
}
cnt[s[st]-'a']--;
if(cnt[s[st]-'a'] == 0) now--;
st++;
}
printf("%d\n",mi);
}
int main() {
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
1007-Function
題意:
思路:
可以發現\(g(x)_{max} = g(99999) = 54\),所以\(g(x)\)至多\(54\)個不同的值,那么當\(g(x)的值確定之后\),這個函數的圖形就是一個二次函數的一些不連續的點(因為不是所有的\(x \ g(x)\) = 當前枚舉的值)
所以直接枚舉\(g(x)\)的值,然后分類討論這個二次函數的圖像的樣子,\(a=0,a<0,a>0\)即可確定函數的最小值。
預處理出來\(g(x)\)=枚舉值的\(x\)有哪些,每次找最值只需要在其中二分即可,但是對稱軸位置處可能不存在\(x\),所以我們只需要二分第一個\(\ge \ x\)的位置,和它前面的位置去這兩個位置處的最小值即可(\(a>0\)的情況)
有一個小細節就是我們預處理的是\(1 - 1000000\)的值,但是每次會給出一個N來固定上限,所以每次對於一個特定的\(N\)來說,要確定一個上界,二分或者直接取邊界的操作都不能直接超過這個上界
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-6;
const int N = 60;
std::vector<ll>vec[N];
ll cal(ll x) {
ll sum = 0;
while(x) {
sum += x % 10;
x /= 10;
}
return sum;
}
void solve() {
ll ans = INF;
ll A,B,C,D,N;
scanf("%lld%lld%lld%lld%lld",&A,&B,&C,&D,&N);
//f(x) = Ax^2g(x) + Bx^2 + Cxg(x)^2 + Dxg(x)
//(Ag(x) + B)x^2 + (Cg(x)^2 + Dg(x))x
for(ll i = 1;i <= 54;i ++) {
if(sz(vec[i]) == 0 || vec[i][0] > N) continue;
ll a = A * i + B,b = C * i * i + D * i;
ll r = upper_bound(vec[i].begin(),vec[i].end(),N) - vec[i].begin();
//因為預處理的是1-1e6,這是一個wa點
if(a <= 0) {
ll j1 = vec[i][0];
ll j2 = vec[i][r-1];
ans = min(ans,min(a * j1 * j1 + b * j1,a * j2 * j2 + b * j2));
}
else if(a > 0) {
ll mid = b / (-2 * a);
std::vector<ll>::iterator j1 = lower_bound(vec[i].begin(),vec[i].begin() + r,mid),j2;
if(j1 == vec[i].begin() + r) {//處理一個找不到的情況
j1 = j2 = prev(j1);
}
else {
if(j1 == vec[i].begin()) j2 = j1;
else j2 = prev(j1);
}
ll x1 = *j1,x2 = *j2;
ans = min(ans,min(a * x2 * x2 + b * x2,a * x1 * x1 + b * x1));
}
}
printf("%lld\n",ans);
}
int main() {
for(ll i = 1;i <= 1000000;i ++) {
vec[cal(i)].pb(i);
}
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
1011-Shooting Bricks
題意:
\(n\)行\(m\)列的矩形,每個格子的內部都有一個磚塊,現在有一把手槍和\(k\)發子彈,一發子彈可以打碎一個磚塊,你只能從每一列的最下面一行開始打,每個磚塊被擊碎后都會有相應的獎勵分數\(f_{i,j}\),同時有些磚塊被打碎后再獎勵分數的基礎上還會獎勵一發子彈,\(c_{i,j} = Y\)代表打碎后會獎勵一發子彈,\(c_{i,j} = N\)代表打碎后不會獎勵子彈。
讓你求出初始有\(k\)發子彈能獲得的最大的分數是多少。
思路:
因為每塊磚有\(Y/N\)的兩種狀態,要考慮最后一顆子彈打到這個磚塊的兩種不同的情況,\(dp[i][j][0]\)表示\(1-i\)列中,消耗了\(j\)發子彈,且最后一發子彈打在了\(N\)磚塊上的情況,\(dp[i][j][1]\)表示\(1-i\)列中,消耗了\(j\)發子彈,且最后一發子彈打在了\(Y\)磚塊上的情況。
注意這里用的是消耗這個詞,既可以認為打到\(Y\)磚塊上時不消耗子彈的。
我們還需要提前處理一個\(w\)數組,\(w[i][j][0/1]\)表示第\(i\)列消耗了\(j\)發子彈,且最后一發子彈打到了\(N/Y\)上。
這里可以把最后一發子彈打到了\(Y/N\)上換一種說法,即當前枚舉的前\(i\)列中是否包含了能夠打出的最后一發子彈,因為打到了\(Y\)上相當於我們又獲得了一枚子彈,打到了\(N\)上就相當於沒了。
我們把\(i\)從\(1-m\)枚舉第\(i\)列,\(j\)從\(0-k\)枚舉,當前總共花費了多少的子彈,\(nowcol\)從\(0-min(j,n)\)枚舉當前第\(i\)列消耗了多少發子彈,類似於一個做背包的過程。
\(dp[i][j][1]\),即前\(i\)列均不包含最后一發子彈,那么只能從\(dp[i-1][j-nowcol][1],w[i][nowcol][1]\)這兩個狀態來轉移過來,即前\(i-1\)列不包含最后一發子彈,第i列也不包含。
而\(dp[i][j][0]\)則可從兩個不同的狀態對轉移過來,\((dp[i-1][j-nowcol][0],w[i][nowcol][1]),(dp[i-1][j-nowcol][1],w[i][nowcol][1])\)。
再加上一些判斷小細節,即最后一發子彈轉移的狀態一定要分配到子彈,因為最后一發子彈一定是會實實在在消耗掉一顆子彈的。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 210;
//dp[i][j][0]表示1-i列共消耗了j發子彈且最后一發子彈打到了N磚塊上 也就是最后一發子彈在當前列
//dp[i][j][1]表示1-i列共消耗了j發子彈且最后一發子彈打到了Y磚塊上 也就是最后一發子彈不在當前列
//w[i][j][0]表示第i列消耗了了j發子彈且最后一發子彈打到了N磚塊上能獲得的最大的價值
//w[i][j][1]表示第i列消耗了了j發子彈且最后一發子彈打到了Y磚塊上能獲得的最大的價值
int n,m,k;
char ch;
int dp[N][N][2],w[N][N][2],a[N][N],can[N][N];
void solve() {
scanf("%d%d%d",&n,&m,&k);
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= m;j ++) {
scanf("%d %c",&a[i][j],&ch);
if(ch == 'Y') can[i][j] = 1;
}
}
for(int i = 1;i <= m;i ++) {
int used = 0;
for(int j = n;j >= 1;j --) {
if(can[j][i] == 1) {
w[i][used][1] += a[j][i];
}
else {
++used;
w[i][used][1] = w[i][used-1][1] + a[j][i];
w[i][used][0] = w[i][used-1][1] + a[j][i];
}
}
}
for(int i = 1;i <= m;i ++) {
for(int j = 0;j <= k;j ++) {
for(int nowcol = 0;nowcol <= min(n,j);nowcol ++) {
dp[i][j][1] = max(dp[i][j][1],dp[i-1][j-nowcol][1] + w[i][nowcol][1]);
if(j - nowcol > 0)
dp[i][j][0] = max(dp[i][j][0],dp[i-1][j-nowcol][0] + w[i][nowcol][1]);
if(nowcol > 0)
dp[i][j][0] = max(dp[i][j][0],dp[i-1][j-nowcol][1] + w[i][nowcol][0]);
}
}
}
printf("%d\n",dp[m][k][0]);
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= m;j ++) {
can[i][j] = 0;
}
}
for(int i = 1;i <= m;i ++) {
for(int j = 0;j <= k;j ++) {
dp[i][j][0] = dp[i][j][1] = w[i][j][0] = w[i][j][1] = 0;
}
}
}
int main() {
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}