1. 程式人生 > 其它 >字串不定求和 洛谷P1874題解

字串不定求和 洛谷P1874題解

技術標籤:洛谷演算法

kkk大佬給出了思路,但沒給出具體程式碼,題解裡只有用P寫的,所以這裡我補充一個C黨解法
傳送門:
洛谷P1874
題目如下:
在這裡插入圖片描述
說實話我第一眼看這道題是很懵比了,看了題解更懵比,什麼二維dp,什麼深搜+剪枝…
各路神仙各顯神通,身為蒟蒻的我戰戰兢兢,如履薄冰。在各路神仙的題解中,排行第一的是kkk大佬的一句話:
在這裡插入圖片描述
這種方法細想,確實妙哉。下面我們就聊聊這種想法。


把題目簡化一下就是
輸入一個數字字串,問怎樣組合使和為n。
例如:123456 如果它的組合方式是 1+2+3+45+6 就可以輸出57 。但這道題反著來了,它先給出n(57)問你組合方式是怎樣的。

把題目看到這裡,本蒟蒻有了點想法:暴力列舉 ’ + ‘ 的位置,判斷是否存在即可。

這個思路可行,但我不會程式碼實現啊!
這樣寫的困難在於,如何列舉位置?因為+的個數是不確定的,位置也是不確定的,如果暴力出奇跡要寫很多程式碼(我太懶了 ACMer永不加班!)

所以核心問題就出來了:枚舉出所有位置的值。

於是有大佬給出了這種想法:(絕妙的舞姿)

	for(int i=1;i<=str.length();i++)
	{
		f[i][i]=s[i];
		for(int j=i;j<=str.length()&&j-i<=11;j++)f[i][j]=f[i][j-1]*10+s[j];
	}

如果列舉所有位置很困難,為什麼不分治成若干個位置呢?

本段程式碼的精髓就是用二維度陣列實現了記憶位置並且儲存了區間值。
那麼之後我們只需要列舉一下所有情況即可。


利用搜索實現列舉

void dfs(int now,int befor,int sum,int t,int len)
{
	///cout<<now<<' '<<sum<<' '<<f[befor][now]<<endl;
	if(sum+f[befor][now]>n)return;
	if(now==len&&sum+f[befor][now]!=n)return;
	if(now==len&&
sum+f[befor][now]==n) {minn=min(minn,t);return;} dfs(now+1,now+1,sum+f[befor][now],t+1,len); dfs(now+1,befor,sum,t,len); }

AC程式碼如下:

#include<bits/stdc++.h>
using namespace std;
int n;
int minn=0x3f3f3f3f;
int f[45][45],s[45]; 
void dfs(int now,int befor,int sum,int t,int len)
{
	///cout<<now<<' '<<sum<<' '<<f[befor][now]<<endl;
	if(sum+f[befor][now]>n)return;
	if(now==len&&sum+f[befor][now]!=n)return;
	if(now==len&&sum+f[befor][now]==n)
	{minn=min(minn,t);return;} 
	dfs(now+1,now+1,sum+f[befor][now],t+1,len);
	dfs(now+1,befor,sum,t,len);
}
int main()
{
	ios::sync_with_stdio(false);
	string str;
	cin>>str;
	for(int i=0;i<str.length();i++)s[i+1]=str[i]-'0';
	for(int i=1;i<=str.length();i++)
	{
		f[i][i]=s[i];
		for(int j=i;j<=str.length()&&j-i<=11;j++)
		{
			f[i][j]=f[i][j-1]*10+s[j];
		}
	}
	cin>>n;
	dfs(1,1,0,0,str.length());
	if(minn==0x3f3f3f3f)cout<<-1<<endl;
	else cout<<minn<<endl;
	return 0;
 }