这几道经典的题本不应再由本菜啰嗦,无奈手痒总想贴点代码~
POJ1141 括号的匹配
dp[i][j]表示从i到j使括号匹配完整的最少需要添加的括号,有
dp[i][i]=1;
dp[i][j]=min(dp[i][k],[k+1][j]);
当s[i]=='(',s[j]==')'或者s[i]=='[',s[j]==']'时,dp[i][j]=min(dp[i][j],dp[i+1][j-1]);
记录每个dp[i][i]添加的部分是什么,添的位置在前还是在后,在dp的过程中,记录dp的路径,dp后沿路径得解;
#include
#include
#include
using namespace std;
const int NN=250;
int n,dp[NN][NN],r[NN][NN];
char s[NN],ad[NN];
void dfs(int i,int j)
{
if (i>j) return;
if (i==j)
{
if (r[i][i]==-1) putchar(s[i]),putchar(ad[i]);
else putchar(ad[i]),putchar(s[i]);
return;
}
if (r[i][j]==0) { putchar(s[i]); dfs(i+1,j-1); putchar(s[j]); }
else { dfs(i,r[i][j]); dfs(r[i][j]+1,j); }
}
int main()
{
int l,i,j,k,tmp1,tmp2;
s[0]='0';
gets(s+1);
n=strlen(s)-1;
memset(dp,0,sizeof(dp));
for (i=1; i<=n; i++)
{
dp[i][i]=1;
if (s[i]=='(') { ad[i]=')'; r[i][i]=-1; }
if (s[i]=='[') { ad[i]=']'; r[i][i]=-1; }
if (s[i]==')') { ad[i]='('; r[i][i]=-2; }
if (s[i]==']') { ad[i]='['; r[i][i]=-2; }
}
for (l=1; l
POJ2288 状态压缩入门,不解释不代表此题不经典不好,就是太经典的状态压缩了
#include
#include
#include
using namespace std;
typedef long long LL;
int n,m;
LL dp[2][1<<12];
inline bool ok(int x,int y)
{
int i,j,k;
for (k=1; k<=m; k++)
{
i=x&1; j=y&1;
x>>=1; y>>=1;
if (i && j) return false;
if (!i && !j)
{
if (k>=1;
y>>=1;
k++;
}
else return false;
}
}
return true;
}
int main()
{
int i,j,t,c,lim;
while (scanf("%d%d",&n,&m),n|m)
{
if ((n&1) && (m&1)) { printf("0\n"); continue; }
lim=(1<
POJ1112 对反图的连通分量染色,两组分组背包,其实也不是太好想到
#include
#include
#include
using namespace std;
const int NN=110;
int n,bn,flag,mp[NN][NN],color[NN],belong[NN],cnt[NN][2];
int ans[NN][NN],id[NN][NN],dp[NN][NN];
void dfs(int u,int c)
{
color[u]=c;
belong[u]=bn;
cnt[bn][c]++;
for (int i=1; i<=n; i++)
{
if (!mp[u][i]) continue;
if (color[i]==-1) dfs(i,c^1);
else if (color[i]==c) { flag=0; return; }
}
}
void get_ans(int i,int k)
{ if (i==0) return;
ans[i][id[i][k]]=1;
ans[i][id[i][k]^1]=0;
get_ans(i-1,k-cnt[i][id[i][k]]);
}
int main()
{
int i,j,k;
scanf("%d",&n);
memset(mp,1,sizeof(mp));
for (i=1; i<=n; i++)
{
mp[i][i]=0;
while (scanf("%d",&j),j)
mp[i][j]=0;
}
for (i=1; i<=n; i++)
for (j=1; j<=n; j++) if (mp[i][j])
mp[j][i]=1;
bn=0; flag=1;
memset(color,-1,sizeof(color));
memset(cnt,0,sizeof(cnt));
for (i=1; i<=n && flag; i++) if (color[i]==-1) bn++,dfs(i,0);
if (!flag) { puts("No solution"); return 0; }
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for (i=1; i<=bn; i++)
for (j=0; j<=1; j++)
{
for (k=(n+1)/2; k>=cnt[i][j]; k--) if (dp[i-1][k-cnt[i][j]])
{
dp[i][k]=1;
id[i][k]=j;
}
}
for (k=(n+1)/2; k; k--) if (dp[bn][k]) break;
get_ans(bn,k);
printf("%d",k);
for (i=1; i<=n; i++) if (ans[belong[i]][color[i]]) printf(" %d",i);
printf("\n%d",n-k);
for (i=1; i<=n; i++) if (!ans[belong[i]][color[i]]) printf(" %d",i);
printf("\n");
return 0;
}
POJ1848 树形DP
#include
#include
#include
#include
using namespace std;
const int NN=110;
const int INF=16843009;
int n,ans=0,flag=0;
int dp[NN][3];
bool vis[NN]={0};
vector adj[NN];
void dfs(int u)
{
int tmp,i;
int cnt0=0;
int cnt1=INF;
int cnt2=INF;
int cnt3=INF;
vis[u]=true;
for (i=0; in) printf("-1\n");
else printf("%d\n",dp[1][0]);
return 0;
}
ZOJ1234 简单线性DP(滚动数组)
将原始数据的筷子长按从长到短存后,
记dp[i][j][0](i>=3*j)为前i个筷子分为j组的最小badness和,不使用第i个筷子;dp[i][j][1](i>=3*j)为前i个筷子分为j组的最小badness和,使用第i个筷子。
有 dp[i][j][0]=min{ dp[i-1][0],dp[i-1][1] }; dp[i][j][1]=min{ dp[i-2][0],dp[i-2][j-1][1] }+(a[i-1]-a[i])^2;
#include
#include
#include
using namespace std;
int a[5005],dp[3][1002][2];
int main()
{
int n,m,i,j,c,k1,k2,T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&m,&n);
m+=8;
for (i=n; i; i--) scanf("%d",&a[i]);
memset(dp,0x7f,sizeof(dp));
dp[0][1][1]=(a[2]-a[3])*(a[2]-a[3]);
for (i=0; i<=2; i++) dp[0][0][0]=dp[i][0][1]=0;
for (i=4; i<=n; i++)
{
c=i%3; k1=(c+2)%3; k2=(c+1)%3;
for (j=1; j<=i/3 && j<=m; j++)
{
dp[c][j][0]=min(dp[k1][j][0],dp[k1][j][1]);
dp[c][j][1]=min(dp[k2][j-1][0],dp[k2][j-1][1])+(a[i-1]-a[i])*(a[i-1]-a[i]);
}
}
printf("%d\n",min(dp[c][m][0],dp[c][m][1]));
}
return 0;
}
POJ1947 树形DP+背包
#include
#include
#include
#include
using namespace std;
const int NN=160;
vector adj[NN];
bool vis[NN]={0};
bool mark[NN][NN]={0};
int n,p,dp[NN][NN];
void dfs(int u)
{
vis[u]=true;
int size=adj[u].size();
dp[u][1]=size;
mark[u][1]=true;
for (int i=0; i!=size; i++)
{
int v=adj[u][i];
if (vis[v]) continue;
dfs(v);
for (int j=n; j; j--)
for (int k=1; kdp[u][j-k]+dp[v][k]-2) dp[u][j]=dp[u][j-k]+dp[v][k]-2;
}
}
}
int main()
{
int u,v;
scanf("%d%d",&n,&p);
for (int i=1; i
POJ1390 DP
首先将序列变成元素有两个属性(颜色a,个数c)的新序列。在做的过程中,原本设计了一个二维的状态,后来发现不好做。考虑到消除了一个字串后,可能使得相同颜色的元素合并到了一起,然而最后一起删除的字串在最原始的序列中的位置不一定是连续的,这里我说的连续的意思是,比如这个例子:{1 2 2 1 2 2 1},最好的做法是先取中间的1,再取4个2,最后一起取两边的2个1。最后一步所取的2个1在原序列中的中间还有一个1,也就是这两个1不连续。序列更复杂时,最优解中某一步所取的字串来自的位置更是各种情况都有,二维的状态(dp[i][j]表示新序列i到j取完所得价值)信息太少,转移时要就所取字串的位置来枚举所有的可能的组合,很是复杂。估计很多人思维和我一样。虽然也想了要加维,又不好从何入手。
#include
#include
#include
#include
#include
using namespace std;
typedef pair pii;
const int N = 210;
int n, a[N], c[N], last[N], prev[N], dp[N][N][N];
void init() {
int x, y = -1, k = 0;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> x;
if (x == y) c[k-1]++;
else a[k]= x, c[k++] = 1;
y = x;
}
n = k;
memset(last, -1, sizeof(last));
for (int i = 0; i < n; i++) {
prev[i] = last[a[i]];
last[a[i]] = i;
}
memset(dp, -1, sizeof(dp));
}
int DP(int i, int j, int k) {
if (dp[i][j][k] != -1) return dp[i][j][k];
if (i > j) return dp[i][j][k] = -10000000;
if (i == j) return dp[i][j][k] = (c[i]+k) * (c[i]+k);
int ret = DP(i, j-1, 0) + (c[j]+k) * (c[j]+k);
for (int p = prev[j]; p >= i; p = prev[p]) {
int tmp = DP(i, p, c[j]+k) + DP(p+1, j-1, 0);
ret = max(ret, tmp);
}
return dp[i][j][k] = ret;
}
int main() {
int cas;
cin >> cas;
for (int i = 0; i < cas; i++) {
init();
cout << "Case " << i+1 << ": " << DP(0, n-1, 0) << endl;
}
return 0;
}