1.洛谷 P4342 IOI1998 Polygon
我的博客
2.洛谷 P4290 HAOI2008 玩具取名
题意
某人有一套玩具,并想法给玩具命名。首先他选择 W, I, N, G
四个字母中的任意一个字母作为玩具的基本名字。然后他会根据自己的喜好,将名字中任意一个字母用 W, I, N, G
中任意两个字母代替,使得自己的名字能够扩充得很长。
现在,他想请你猜猜某一个很长的名字,最初可能是由哪几个字母变形过来的。
四个整数 W , I , N , G W, I, N, G W,I,N,G,表示每一个字母能由几种两个字母所替代:
-
接下来 W W W 行,每行两个字母,表示
W
可以用这两个字母替代。 -
接下来 I I I 行,每行两个字母,表示
I
可以用这两个字母替代。 -
接下来 N N N 行,每行两个字母,表示
N
可以用这两个字母替代。 -
接下来 G G G 行,每行两个字母,表示
G
可以用这两个字母替代。 -
最后一行一个长度不超过 L L L 的字符串。表示这个玩具的名字。
一行字符串,输出该名字可能由哪一个字母变形而得到。(按照 W, I, N, G
的顺序输出)
如果给的名字不能由任何一个字母变形而得到则输出 The name is wrong!
。
L ≤ 200 L \leq 200 L≤200, W , I , N , G ≤ 16 W, I, N, G \leq 16 W,I,N,G≤16。
思路
其实就是原字符串中,找其中两个合并成特定的一个字母,问最后能否变成只有一个字母。合并问题,想到用区间 dp。
不妨给 4 4 4 个字母编号: W , I , N , G \rm W,I,N,G W,I,N,G 分别对应 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4。设 f i , j , x f_{i,j,x} fi,j,x 表示区间 [ i , j ] [i,j] [i,j] 的字符,是否可以合并成编号为 x x x 的字符。
考虑转化为子问题:将区间 [ i , j ] [i,j] [i,j] 拆分成 [ i , k ] [i,k] [i,k] 和 [ k + 1 , j ] [k+1,j] [k+1,j],并且分别期望用编号为 p q , p , q pq,p,q pq,p,q 的字母来覆盖(这变量够直观吧)。
如果左右区间都可以被完全替换为期望字符,并且编号为 p q pq pq 的字母可以被两个编号分别为 p , q p,q p,q 的字母并起来替代,那么 f i , j , x = 1 f_{i,j,x}=1 fi,j,x=1。
至于关系就很好处理了。我的代码是懒人写法:
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=203;
ll m[5];
string s;
bool vis[5][5][5],f[N][N][5];//f(i,j,x):区间[i,j]能否替换成字母编号x
ll chg(char c)
{if(c=='W')return 1;if(c=='I')return 2;if(c=='N')return 3;return 4;
}
char print(ll i)
{if(i==1)return 'W';if(i==2)return 'I';if(i==3)return 'N';return 'G';
}
int main()
{ios::sync_with_stdio(0);for(int i=1;i<=4;i++)cin>>m[i];for(int i=1;i<=4;i++){for(int j=1;j<=m[i];j++){string t;cin>>t;vis[i][chg(t[0])][chg(t[1])]=1;}}cin>>s;ll n=s.size();s='*'+s;for(int i=1;i<=n;i++)f[i][i][chg(s[i])]=1;for(int len=2;len<=n;len++){for(int i=1;i+len-1<=n;i++){ll j=i+len-1;for(int k=i;k<j;k++)for(int pq=1;pq<=4;pq++)for(int p=1;p<=4;p++)for(int q=1;q<=4;q++)if(vis[pq][p][q]&&f[i][k][p]&&f[k+1][j][q])f[i][j][pq]=1;}}bool flag=0;for(int pq=1;pq<=4;pq++){if(f[1][n][pq])cout<<print(pq);flag|=f[1][n][pq];}if(!flag)puts("The name is wrong!");return 0;
}
3.CF607B Zuma
题意
Genos \texttt{Genos} Genos 最近在他的手机上下载了祖玛游戏。在祖玛游戏里,存在 n n n 个一行的宝石,第 i i i 个宝石的颜色是 a i a_i ai。这个游戏的目标是尽快的消灭一行中所有的宝石。
在一秒钟, Genos \texttt{Genos} Genos 能很快的挑选出这些有颜色的宝石中的一个回文的、连续的子串,并将这个子串移除。每当一个子串被删除后,剩余的宝石将连接在一起,形成一个新的行列。
求把整个宝石串都移除的最短时间。
1 ≤ n ≤ 500 1 \le n \le 500 1≤n≤500。
思路
合并就考虑区间 dp,设 f i , j f_{i,j} fi,j 表示区间 [ i , j ] [i,j] [i,j] 能否被完全消除,那么有几个明显的结论:
- f i , i = 1 f_{i,i}=1 fi,i=1
- f i , i + 1 = { 1 , a i = a i + 1 2 , a i ≠ a i + 1 f_{i,i+1}=\left\{\begin{matrix} 1,a_i=a_{i+1}\\ 2,a_i\neq a_{i+1} \end{matrix}\right. fi,i+1={1,ai=ai+12,ai=ai+1
- f i , j = f i + 1 , j − 1 , a i = a j f_{i,j}=f_{i+1,j-1},a_i=a_j fi,j=fi+1,j−1,ai=aj
前两点用于初始化。第三点作为特殊情况;对于朴素情况,枚举断点 k ∈ [ i , j ) k\in[i,j) k∈[i,j) 就好:
f i , j = min { f i , k + f k + 1 , j } f_{i,j}=\min\{f_{i,k}+f_{k+1,j}\} fi,j=min{fi,k+fk+1,j}
只需讨论区间长度为 3 3 3 的就好了。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=502,inf=0x3f3f3f3f;
ll n,a[N];
ll f[N][N];//f(i,j):区间[i,j]全部消除的最短时间
bool palin(ll l,ll r)
{for(int i=l,j=r;i<=j;i++,j--)if(a[i]!=a[j])return 0;return 1;
}
int main()
{scanf("%lld",&n);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);for(int i=1;i<=n;i++){f[i][i]=1;if(a[i]==a[i+1])f[i][i+1]=1;else f[i][i+1]=2;for(int j=i+2;j<=n;j++)f[i][j]=inf;}for(int len=3;len<=n;len++){for(int i=1;i+len-1<=n;i++){ll j=i+len-1;if(a[i]==a[j])f[i][j]=f[i+1][j-1];for(int k=i;k<j;k++)f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);}}printf("%lld",f[1][n]);return 0;
}